I've got some objects that look like below. I've got some property units, which contain one or more tenants, and the tenant object has a one-to-one relationship with users
class User(Base):
"""
Application's user model.
"""
__tablename__ = 'usr_users'
usr_user_id = Column(Integer, primary_key=True)
usr_email = Column(Unicode(50))
_usr_password = Column('password', Unicode(64))
usr_groups = Column(Unicode(256))
usr_activated = Column(Boolean)
tenant = relationship("Tenant", uselist=False, backref="usr_users")
class Tenant(Base):
__tablename__ = 'ten_tenants'
ten_tenant_id = Column(Integer, primary_key=True)
ten_ptu_property_unit_id = Column(Integer, ForeignKey('ptu_property_units.ptu_property_unit_id'))
ten_usr_user_id = Column(Integer, ForeignKey('usr_users.usr_user_id'))
class PropertyUnit(Base):
__tablename__ = 'ptu_property_units'
ptu_property_unit_id = Column(Integer, primary_key=True)
ptu_pty_property_id = Column(Integer, ForeignKey('pty_propertys.pty_property_id'))
tenants = relationship("Tenant")
I'm attempting to pull all of the units for a property, including the tenant information and the email from the user table.
I managed to get one join pretty easy:
rows = DBSession.query(PropertyUnit).join(Tenant).filter(PropertyUnit.ptu_pty_property_id==request.GET['property_id']).order_by(PropertyUnit.ptu_number)
units = rows.all()
And I'm displaying in the template like this:
% for unit in units:
<%
tenants = unit.tenants
%>
<tr>
<td>${unit.ptu_number}</td>
<td>
% for tenant in tenants:
${tenant.ten_usr_user_id},
% endfor
</td>
</tr>
% endfor
So far so good. Now I need to pull the user information from the tenant foreign key, so I thought I could just tack on another join:
rows = DBSession.query(PropertyUnit).join(Tenant).join(User).filter(PropertyUnit.ptu_pty_property_id==request.GET['property_id']).order_by(PropertyUnit.ptu_number)
units = rows.all()
This appears to work in the SQL logs, as it generates the right SQL, but I'm unable to get to the data in the same way that I did the first time. This fails:
% for unit in units:
<%
tenants = unit.tenants
%>
<tr>
<td>${unit.ptu_number}</td>
<td>
% for tenant in tenants:
<%
user = tenant.User
%>
${tenant.ten_usr_user_id},
% endfor
</td>
</tr>
% endfor
So, the above code throws a "'Tenant' object has no attribute 'User'" error.
How do I get to that user join?
There's no attribute User on Tenant because you didn't define one. You called it usr_users in the backref, so you should access it as tenant.usr_users.
Related
Assume the following example where we have two models each referring to their respective sqlite tables:
class supplier_invoices(db.Model):
id = db.Column('id', db.Integer, primary_key = True)
at_date = db.Column(db.String(100))
at_shortnumber = db.Column(db.String(100))
def __repr__(self):
return str(self.at_date)
class product_inventory(db.Model):
id = db.Column('id', db.Integer, primary_key = True)
pi_shortnumber = db.Column(db.String(100))
pi_lot_name = db.Column(db.String(100))
pi_at_id = db.Column(db.Integer, db.ForeignKey('supplier_invoices.id'),
nullable=False)
at_date = relationship("supplier_invoices")
def __init__(self, id, pi_shortnumber):
self.id = id
self.pi_shortnumber = pi_shortnumber
#app.route('/pro_inv/')
def product_inv():
return render_template('product_inventory.html',
product_query = product_inventory.query.order_by(product_inventory.pi_shortnumber.desc()).limit(20).all())
Then assume we are using the following jinja2 template to display the query in a table form:
{% for pq in product_query %}
<tr>
<td>
{{ pi.at_date }}
</td>
<td>
{{ pi.pi_lot_name }}
</td>
<td>
{{ <!-- at_shortnumber --> }} <!-- ******* HOW TO GET SECOND COLUMN DATA HERE? -->
</td>
<td>
</tr>
{% endfor %}
As you can see retrieving the first column data pi.at_date using sqlalchemy relationships is relatively straightforward. The resulting jinja2 table displays at_date from the supplier_invoices child table along side the corresponding pi_lot_name column data from the parent product_inventory table it is joined with.
However, this relationship only returns the at_date column.
How would one go about retrieving the at_shortnumber column in the same model in order to display it along side the at_date column?
Obviously, one solution would be to create a new separate model relationship but this seems cumbersome to me since if one wants to retrieve let's say 10 columns we would have to have establish 10 separate model relationships.
Would anyone have any suggestions as to how to retrieve multiple columns' data from a related table using the same model? Or possibly a completely different way of achieving the same result?
Thank you in advance!
edit: additionally, how would one assign a parent name within the parent model class to the child column data so that one can manipulate the data directly from the model class? For example, if one wished to use : concat = column_property(pi_shortnumber + "_" + pi_lot_name + "_" + at_shortnumber )
You can access it as pd.at_date.at_shortnumber. From your code pq.at_date returns at_date value of the coressponding SupplierInvoices which is correct, what it actually does is pq.at_date.at_date. But you have it sharing names with supplier_invoices.at_date hence the confusion. Either change the column name for supplier_invoices.at_date or the relationship name for product_inventory.at_date.
I am trying to display a doctors first name from another table that is linked by foreign key. I can get the doctor_id to display but cannot get his name to display.
I looked at this solution
reading from joined query in flask-sqlalchemy
but it is slightly different as I am querying from the other side and cannot use the backref value as a reference. I have removed the irrelevant code.
class Appointment(db.Model):
id = db.Column(db.Integer, primary_key=True)
patient_id = db.Column(db.Integer, db.ForeignKey('patient.id'),
nullable=False)
doctor_id = db.Column(db.Integer, db.ForeignKey('doctor.id'),
nullable=False)
class Doctor(db.Model):
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(30), unique=False, nullable=False)
appointments = db.relationship('Appointment', backref =
db.backref('doctor',lazy=True))
and the query
all_appmts = db.session.query(Appointment)
.filter_by(patient_id=id)
.join(Doctor)
result =appointments_schema.dump(all_appmts)
return render_template('patient.html', all_appointments=result.data)
and this is what i tried
{% for a in all_appointments %}
<td>{{ a.doctor_id.first_name }}</td>
{% endfor %}
The doctor name displayed should be based on the the doctor id for that appointment.
Here is the marshmallow part.
class AppointmentSchema(ma.Schema):
class Meta:
# Fields to expose
fields = ('id','start_datetime', 'end_datetime', 'title',
'patient_id', 'doctor_id')
appointments_schema = AppointmentSchema(many=True)
You are trying to access doctor_id.first_name. But the name of the relationship is doctor. If you are converting the result of the query to a list of dicts, then you should serialize the appointment.doctor relationship also, so that the dict looks like
{
id: 12,
doctor: {
id: 34
}
}
Then you can access it like this
<td>{{ a.doctor.first_name }}</td>
But if you are just planning to use it in jinja template, then what is the need to serialize the objects? Instead you can just pass the result of the query.all() to the template. Jinja can directly access the python objects and show the data. So instead of result =appointments_schema.dump(all_appmts), try doing this
all_appmts = db.session.query(Appointment)
.filter_by(patient_id=id)
.join(Doctor)
return render_template('patient.html', all_appointments=all_aptmts.all())
And then keep the jinja template as the same
{% for a in all_appointments %}
<td>{{ a.doctor.first_name }}</td>
{% endfor %}
It will work
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')
I am trying to display data from MySQL via Flask-SQLAlchemy query and change foreign key (category_id) into name assign to the category_id. To be more precise -
With the query I want to display item with the name from category Table, not the category_id.
Here is my code:
class MyEnum(enum.Enum):
piece = "piece"
kg = "kg"
class Category(db.Model):
__tablename__ = 'category'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(25), nullable=False)
class Product(db.Model):
__tablename__ = 'product'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30), nullable=False)
quantity = db.Column(db.Integer, nullable=False)
product_type = db.Column(db.Enum(MyEnum), nullable=False)
category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
description = db.Column(db.String(255))
category = db.relationship("Categ{{user.id}}ory",
backref=('products'))
def __init__(self,name, quantity, product_type, category_id, description):
self.name = name
self.quantity = quantity
self.product_type = product_type
self.category_id = category_id
self.description = description
db.create_all()
db.session.commit()
#app.route('/grocery-list', methods=['GET'])
def display_data():
data = Product.query.all()
category_name = db.session.query(Product).join(Category, Product.category_id == Category.name)
return render_template('query_database.html', data=data, category_name = category_name)
#query_database.html
<body>
{% for user in data %}
<li>{{user.id}}. {{user.name}} {{user.quantity}} {{user.product_type}} Category {{user.category_id}} {{user.description}}</li>
{% endfor %}
{{ category_name }}
</body>
Result of query_Database.html:
3. Ziemniaczki 2 MyEnum.kg Category 1 Na obiad
SELECT product.id AS product_id, product.name AS
product_name,product.quantity AS product_quantity, product.product_type AS product_product_type, product.category_id AS product_category_id,
product.description AS product_description FROM product INNER JOIN category ON product.category_id = category.name
Questions:
1) How to create such query? I got overview how should this look like in pure SQL but I can't find equivalent in documentation of SqlAlchemy :
select p.name, c.name
from product as p
join category as c
on c.id = p.category_id
2) What MyEnum.kg is doing out there? How to delete the My.Enum from the this view?
EDIT - Success
Just leaving the working code, if someone would ever need so.
#app.route('/grocery-list', methods=['GET'])
def display_data():
data = db.session.query(Product, Category).join(Category).all()
return render_template('query_database.html', data=data)
{% for user, category in data %}
<li>{{user.id}}. {{user.name}} {{user.quantity}} {{user.product_type}} Category {{user.category.name}} {{user.description}}</li>
{% endfor %}
Solution
After joining tables, in template file it's required to unpack the value of the category.name with
{{user.category.name}}
1) How to create such query? I got overview how should this look like in pure SQL but I can't find equivalent in documentation of SqlAlchemy
Here are some links that you might find useful:
Querying with Joins
Query.join()
Using Joins
sqlalchemy: how to join several tables by one query?
Since you've defined the ORM relationship between Product and Category, you can eager load the related categories along with the products:
data = Product.query.options(db.joinedload(Product.category)).all()
and then you can access user.category.name in your template without it emitting new queries. Another, more SQL-esque solution, would be to fetch Product, Category tuples, which seems like what you were after:
# No need to explicitly define the ON clause of the join, unless you
# really want to. SQLAlchemy examines the foreign key(s) and does what
# needs to be done.
data = db.session.query(Product, Category).join(Category).all()
and then in your template you'd unpack the result tuples:
<!-- I don't understand why it's called "user" -->
{% for user, category in data %}
...
{% endfor %}
2) What MyEnum.kg is doing out there? How to delete the My.Enum from the this view?
That's just the string representation of an enum:
In [4]: str(MyEnum.kg)
Out[4]: 'MyEnum.kg'
You'll want to modify {{user.product_type}} to suit your needs, if you're unhappy with it. You've used the SQLAlchemy Enum type and a PEP-435-compliant enumerated class for that column:
When using an enumerated class, the enumerated objects are used both for input and output, rather than strings as is the case with a plain-string enumerated type...
So i have an issue where I am using the flask route to edit a existing tab in a project of mine. What should happen is that my project store two things user and tabs. I have several tabs that are in my database and i want to create edit functionality to it. So i am creating and edit route that will pass the users id and tab id that is being editied. What should happen is that the tabs amount and description that is being edited will appear in the input box for me to edit. it should then change that tab in the database and return to the page that shows all of the tables with the edit.
What is happening is that no matter what tab i click on it shows the first tabs information in the input box and when i submit to edit add and commit the change to the database, it is added a new tab and giving it the values that changed. I will insert 3 peices of code.
** i do have all of the correct sqlalchemy informaiton imported
the first piece of code is the database
class Users(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key = True)
username = Column(String(16), nullable = False, unique = True, index = True)
class Tab_Event(Base):
__tablename__ = 'tab_event'
id = Column(Integer, primary_key = True)
description = Column(String(200))
amount = Column(String(10))#may be changed to a float or decimal value later on during development
user_id = Column(Integer(10), ForeignKey('users.id'))
users = relationship(Users)
The following section of code is the routes that are being used
#app.route('/user/<int:user_id>')
def DisplayUsers(user_id):
user = session.query(Users).filter_by(id = user_id).one()
items = session.query(Tab_Event).filter_by(user_id = user.id)
return render_template('viewTabs.html', user = user, items = items)
#app.route('/newtab/<int:user_id>', methods = ['GET', 'POST'])
def AddNewTab(user_id):
if request.method == 'POST':
newTab = Tab_Event(amount = request.form['amount'], description = request.form['description'], user_id = user_id)
session.add(newTab)
session.commit()
return redirect(url_for('DisplayUsers', user_id = user_id))
return render_template('createTab.html', user_id = user_id)
#app.route('/edit/<int:user_id>/<int:tab_id>', methods = ['GET', 'POST'])
def EditTabEvent(user_id, tab_id):
editTab = session.query(Tab_Event).filter_by(id = user_id).one()
if request.method == 'POST':
if request.form['name']:
editTab.name = request.form['amount']
if request.form['description']:
editTab.description = request.form['description']
session.add(editTab)
session.commit()
return redirect(url_for('DisplayUsers', user_id = user_id))
else:
return render_template('editTab.html', user_id = user_id, tab_id = tab_id, item = editTab)
The last piece of code is the html document that i am using to edit
<html>
<body>
<h1>This page is where new users are made</h1>
<form action="{{url_for('AddNewTab', user_id = user_id, tab_id = item.id)}}" method="POST">
<table>
<tr>
<td>Username</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>Amount</td>
<td><input type="text" name="amount", value="{{item.amount}}"></td>
</tr>
<tr>
<td>Description</td>
<td><input type="text" name="description", value="{{item.description}}"></td>
</tr>
<tr>
<td><input type="Radio">Personal</td>
<td><input type="Radio">Business</td>
</tr>
<tr>
<td><input type="submit" value="Edit Tab"></td>
</tr>
</table>
</form>
</body>
</html>
the repository that has these files is below:
vagrant >> PayUp >> all of the files for this project to run on local machine
https://github.com/omar-jandali/Udacity-Tournament/tree/master/vagrant/PayUp