order_by() a related table with Flask-SQLAlchemy - python

Here are my models:
class Entry(db.Model):
id = db.Column(db.Integer, primary_key=True)
manifest = db.Column(db.String, default=None, nullable=True)
name = db.Column(db.String, default=None, nullable=True)
actions = db.relationship('Action', backref='entry', lazy='dynamic')
class Action(db.Model):
id = db.Column(db.Integer, primary_key=True)
action_date = db.Column(db.DateTime, default=datetime.utcnow, nullable=True)
location = db.Column(db.String, default=None, nullable=True)
entry_id = db.Column(db.Integer, db.ForeignKey('entry.id'))
routes.py:
#app.route('/manifests/<manifest_to_view>')
#login_required
def view_manifest(manifest_to_view):
page = request.args.get('page', 1, type=int)
entries = Entry.query.filter_by(manifest=manifest_to_view).paginate(
page, app.config['POSTS_PER_PAGE'], False)
next_url = url_for('view_manifest', manifest_to_view=manifest_to_view, page=entries.next_num) \
if entries.has_next else None
prev_url = url_for('view_manifest', manifest_to_view=manifest_to_view, page=entries.prev_num) \
if entries.has_prev else None
return render_template("view_manifest.html", title='View Manifest', manifest_to_view=manifest_to_view, entries=entries.items, next_url=next_url, prev_url=prev_url)
And from the template:
{% for entry in entries %}
<td>{{ entry.actions.first().location }}</td>
{% endfor %}
This page displays all rows in the Entry table that share a specific "manifest" (an alphanumeric identifier). So you can see my query in routes.py starts:
entries = Entry.query.filter_by(manifest=manifest_to_view)...
For each row from the Entry table, I also need to display the most recent location from the related Action table, but my current line displays the wrong location:
{{ entry.actions.first().location }}
Is there a way to sort locations by the Action.action_date column using order_by() instead of using first()? Or any way to print the most recent location?
Thanks.

found the answer here: SQLAlchemy - order_by on relationship for join table
I just had to change the relationship in model.py
actions = db.relationship('Action', backref='entry', order_by="desc(Action.id)", lazy='dynamic')

Related

{{ treatment.data }} jinja Treatment model does not have a data field. data not displaying

I ran into this issue while making this application to collect patient data at a dental clinic. I followed this tutorial. I can get some small things in the code to meet my requirement. But It just displays dots instead of the data. When I add {{ treatment.date }} It displays the date the information is created as expected. This is how it looks. When I add {{ treatment.data | pprint }} It dispalays the dot and prints Undefined. I haven't added the css styling yet.
<ul id="treatment">
{%for treatment in user.info%}
<li>{{ treatment.data }}</li>
{%endfor%}
</ul>
This is how it is written in the html
This is how I enter the data to the database
def home():
if request.method == 'POST':
treatment = request.form.get('treatment')
history = request.form.get('history')
future = request.form.get('future')
payment = request.form.get('payment')
if len(treatment) <1 :
flash('Treatment is too short', category='error')
elif len(history) <1 :
flash('History is too short', category='error')
elif len(future) <1 :
flash('Treatment plan is too short', category='error')
elif len(payment) <1 :
flash('Payment is too short', category='error')
else:
new_record=Info(treatment=treatment,history=history,future=future,payment=payment,user_id=current_user.id)
db.session.add(new_record)
db.session.commit()
flash('Record Added!', category='success')
return render_template("home.html", user=current_user)
This is the code in the models.py
from flask_login import UserMixin
from sqlalchemy.sql import func
class Info(db.Model):
id = db.Column(db.Integer, primary_key=True)
date = db.Column(db.DateTime(timezone=True), default=func.now())
history = db.Column(db.String(10000))
treatment = db.Column(db.String(10000))
future = db.Column(db.String(10000))
payment = db.Column(db.Integer)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(150))
tel = db.Column(db.Integer, unique=True)
nic = db.Column(db.Integer, unique=True)
info = db.relationship('Info')```
I hope someone can help.
I expect it to display the data regarding treatment,history,future and payment

Creating a Full-Text Search in Flask [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I am trying to relalize full-text search in flask, but nothing happend, there is nothing in traceback and web-application keep working. Please, could you advise me, where I should make some corrections, I try to realize this one first time and dont have such experience before.
event\forms.py
class SearchForm(FlaskForm):
query = StringField('search', validators=[DataRequired()], render_kw={"class": "form-control"})
submit = SubmitField('Submit', render_kw={'class': 'btn btn-secondary border-0'})
event\models.py
class Event(db.Model):
__searchable__ = ['title', 'description']
__table_args__ = {'extend_existing': True}
id = db.Column(db.Integer, unique=True, primary_key=True)
title = db.Column(db.String(50), nullable=False)
genre = db.Column(db.String(100), nullable=True)
url = db.Column(db.String(100), unique=True, nullable=False)
date_start = db.Column(db.Date, nullable=True)
date_finish = db.Column(db.Date, nullable=True)
description = db.Column(db.String(150), nullable=True)
place = db.Column(db.String(50), nullable=True)
address = db.Column(db.String(100), nullable=True)
price = db.Column(db.String, nullable=True)
img_url = db.Column(db.String(100), nullable=True)
text = db.Column(db.Text, nullable=True)
category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
category = db.relationship('Category', backref='events')
resource_id = db.Column(db.Integer, db.ForeignKey('resource.id'))
resource = db.relationship('Resource', backref='events')
event\views.py
#blueprint.before_request
def before_request():
g.search_form = SearchForm()
#blueprint.route('/search', methods = ['POST'])
def search():
if not g.search_form.validate_on_submit():
return redirect(url_for('event.index'))
return redirect(url_for('event.search_results', query = g.search_form.search.data))
#blueprint.route('/search_results/<query>')
def search_results(query):
search_str = f"%{form.search.data}%"
results = Event.query.filter(Event.name.ilike(search_str), Event.description.ilike(search_str))
return render_template('event/search_results.html',
query = query,
results = results)
event\search_results.html
<!-- extend base layout -->
{% extends "base.html" %}
{% block content %}
{% if results %}
<h3>Here are some results</h3>
{% for result in results %}
{{ result }}
{% endfor %}
{% endif %}
{% endblock %}
Thanks a lot!
I see 2 issues.
On the '/search_results/' route, form.search.data is never defined. Since you included that as a url parameter, you can reference "query" directly in your sqlalchemy query.
The filter clause on your query is currently:
filter(Event.name.ilike(search_str), Event.description.ilike(search_str))
which translates to name and description are both like search_str. I'm assuming that you what you are really looking for is name or description are like search_str.
If that's the case, you can wrap the filter with an "or" clause.
filter(or_(Event.name.ilike(search_str), Event.description.ilike(search_str)))
https://docs.sqlalchemy.org/en/13/core/sqlelement.html#sqlalchemy.sql.expression.or_

'list' object has no attribute 'id'

i get 'list' object has no attribute 'id' error. I dont know why.
#posts.route("/post/<int:post_id>", methods=['GET','POST'])
#login_required
def course_post(post_id):
post=Post.query.get_or_404(post_id)
chapters=Chapter.query.filter_by(post_id=post_id).all()
chapter_id = chapters.id
videos=Videos.query.filter_by(chapter_id=chapter_id).all()
return render_template('course.html', title=post.course_name, post=post, chapters = chapters)
class Chapter(db.Model):
id = db.Column(db.Integer, primary_key= True)
chapter_no = db.Column(db.Integer)
chapter_name = db.Column(db.String(50))
chapter_desc = db.Column(db.String(50))
chapter_date = db.Column(db.DateTime, default=datetime.utcnow)
vi = db.relationship('Videos', backref='topic', lazy=True)
post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)
def __repr__(self):
return f"Chapter('{self.chapter_no}', '{self.chapter_name}', '{self.chapter_desc}', '{self.chapter_date}', '{self.post_id}'"
class Videos(db.Model):
id = db.Column(db.Integer, primary_key= True)
video_no = db.Column(db.Integer)
video_name = db.Column(db.String(50), nullable=False, index=True)
video_file = db.Column(db.String(200))
yout_code = db.Column(db.String(200))
video_description = db.Column(db.Text(100))
uploaded_date = db.Column(db.DateTime, default=datetime.utcnow)
rating = db.Column(db.Integer())
chapter_id = db.Column(db.Integer, db.ForeignKey('chapter.id'), nullable=False)
c_v = db.relationship("Chapter", foreign_keys=chapter_id) #
def __repr__(self):
return f"Videos('{self.video_no}','{self.video_name}', '{self.video_file}', '{self.yout_code}', '{self.video_description}', '{self.uploaded_date}', '{self.rating}', '{self.chapter_id}'"
i am trying to get all the videos for each chapter in the Videos table using chapters.id as foreign key
Please what am i doing wrong ?
Try updating your function like this
#posts.route("/post/<int:post_id>", methods=['GET','POST'])
#login_required
def course_post(post_id):
post=Post.query.get_or_404(post_id)
chapters=Chapter.query.filter_by(post_id=post_id).all()
chapter_id = chapters and chapters[0].id
videos=Videos.query.filter_by(chapter_id=chapter_id).all()
return render_template('course.html', title=post.course_name, post=post, chapters = chapters)
Now it uses first chapter ID it founds. I don't know if this is something you want though.
If you want to get list of chapter ids you can do it like this
chapter_id = [c.id for c in chapters]
But I don't know how ORM library you are using handles filtering by lists.
I added this to the parent class (Chapter) of the model.py
medias = db.relationship('Videos', backref='Chapter', lazy='subquery')
The code below pulls the query and sub query of parent and child. This code does that:
chapters = db.session.query(Chapter).filter_by(post_id=post_id).all()
i.e:
#posts.route("/post/<int:post_id>", methods=['GET','POST'])
#login_required
def course_post(post_id):
post=Post.query.get_or_404(post_id)
#chapters=Chapter.query.filter_by(post_id=post_id).all()
chapters = db.session.query(Chapter).filter_by(post_id=post_id).all()
return render_template('course.html', title=post.course_name, post=post, chapters = chapters)
Then in my template, to loop the chapters and videos for each chapter in the template:
{% for v in chapters %}
<h7><span class="plus">+</span>{{ v.chapter_name }}</h7> <br><br>
<div class="descDiv">
{% for media in v.medias %}
<p class="desc">{{ media.video_name }}</p>
{% endfor %}
</div>
{% endfor %}

How to use many-to-many fields in Flask view?

I am trying to create a blog using Flask. Every post can have multiple tags also every tag could be associated with multiple posts. So I created a many-to-many relationship. My questions is how do i save multiple tags when creating a new post. And since every post can have different number of tags how do i show this is in the form? Also, how can i create new tags along with the post and then use those tags with other posts?
This is models.py -
postcategory = db.Table('tags',
db.Column('posts_id', db.Integer, db.ForeignKey('posts.id')),
db.Column('categories_id', db.Integer, db.ForeignKey('categories.id'))
)
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
content = db.Column(db.Text)
slug = db.Column(db.String, unique=True)
published = db.Column(db.Boolean, index=True)
timestamp = db.Column(db.DateTime, index=True)
categories = db.relationship('Category', secondary=postcategory, backref='posts' )
def __init__(self, title, content):
self.title = title
self.content = content
class Category(db.Model):
__tablename__ = 'categories'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String, index=True)
This is the view i am working on -
def create_article():
if request.method == 'POST':
if request.form.get('title') and request.form.get('content') and request.form.get('slug') and request.form.get('published'):
post = Post(request.form['title'], request.form['content'], request.form['slug'], request.form['published'])
I am sure there is a easy solution and i am just complicating this, but i am new to web development, so please help.
You can pull the categories out of the form with getlist and add them to the Post object. If you have checkboxes like the following:
<form>
<input type="checkbox" name="categories" value="foo">
<input type="checkbox" name="categories" value="bar" checked>
</form>
In your view method you can just do:
categories_from_form = request.form.getlist('categories') # ['bar']
# create Category objects with the form data
categories = [Category(title=title) for title in categories_from_form]
post = Post(request.form['title'], request.form['content'], request.form['slug'], request.form['published'])
post.categories = categories # attach the Category objects to the post
...

SQLAlchemy Query foreignkey field(s)

I am trying to write a view/SQLAlchemy query that will allow me to display data thats linked as a ForeignKey and also where I have a ManyToMany relationship
have the models..
#Samples
class Sample(Base):
__tablename__ = 'samples'
id = Column(Integer, primary_key=True)
name = Column(Unicode)
def __unicode__(self):
return self.name
samples_to_customer = Table('samples_to_customer', Base.metadata,
Column('customer_id', Integer, ForeignKey('customer.id')),
Column('sample_id', Integer, ForeignKey('samples.id'))
)
#Create a cusotmer model to store customer numbers
class Customer(Base):
__tablename__ = 'customer'
id = Column(Integer, primary_key=True)
name = Column(Unicode, unique=True) #this will be the variable used to search the existing db
customer_samples = relationship('Sample', secondary='samples_to_customer', backref='samples')
sales_rep = Column(Integer, ForeignKey('rep.id'))
Rep = relationship('Rep')
def __unicode__(self):
return self.name
# This model will have its own entry view/page a user logs on and enters notes (according to a customer
# number) they then get stored/saved onto the Customers account
class Note(Base):
__tablename__ = 'note'
id = Column(Integer, primary_key=True)
name = Column(Unicode)
pub_date = Column(Date)
customer_no = Column(Integer, ForeignKey('customer.id'))
customer = relationship('Customer')
def __unicode__(self):
return self.name
And the views..
#view_config(route_name='view_customer', renderer='templates/view_customer.jinja2')
def view_page(request):
customer_no = request.matchdict['customer']
cust_slcustm = DBSessionRO.query(Slcustm).filter(Slcustm.customer == customer_no).first()
cust_customer = DBSessionRO.query(Custom).filter(Custom.cu_custref== customer_no).first()
# Return a 404 if a result isn't found, slcustm and customer share one2one relationship on customer_no
if cust_slcustm is None:
return HTTPNotFound('No such customer')
return dict(cust_slcustm=cust_slcustm, cust_customer=cust_customer)
But I cant seem to write a query that will display every sample and note related to a customer number? If anyone could help I would be very grateful :)
Try this
class Note(Base):
__tablename__ = 'note'
id = Column(Integer, primary_key=True)
name = Column(Unicode)
pub_date = Column(Date)
customer_no = Column(Integer, ForeignKey('customer.id'))
customer = relationship('Customer', backref='notes')
#view_config(route_name='view_customer', renderer='templates/view_customer.jinja2')
def view_page(request):
customer_no = request.matchdict['customer']
cust = DBSessionRO.query(Customer).filter(Customer.id == customer_no).first()
print "Sample :", cust.customer_sample
print "Notes :", cust.notes
What ended up working was:
#views.py
#view_config(route_name='view_customer', renderer='templates/view_customer.jinja2')
def view_page(request):
customer_no = request.matchdict['customer']
cust_slcustm = DBSessionRO.query(Slcustm).filter(Slcustm.customer == customer_no).first()
cust_customer = DBSessionRO.query(Custom).filter(Custom.cu_custref== customer_no).first()
cust_books = DBSessionRW.query(Customer).filter(Customer.name == customer_no).first()
# Return a 404 if a result isn't found, slcustm and customer share one2one relationship on customer_no
if cust_slcustm is None:
return HTTPNotFound('No such customer')
return dict(cust_slcustm=cust_slcustm, cust_customer=cust_customer, cust_books=cust_books)
Then creating the template:
{% for sample in cust_books.customer_samples %}
{{ sample.name }}
{% endfor %}

Categories