SQLAlchemy query tables joined with foreign key - python

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...

Related

How to select distinct column values from mysql database in django?

I need to retrieve distinct value from query set and display it on a div.
Here is my model.py:
class Persons(models.Model):
id = models.IntegerField(primary_key=True)
firstname = models.CharField(max_length=100)
lastname = models.CharField(max_length=100)
address = models.CharField(max_length=100)
dob = models.CharField(max_length=100)
salary = models.IntegerField
doj = models.DateField()
class Meta:
db_table = "test"
Here is my view.py:
def calender(request):
distinctyears = Persons.objects.all().values('doj').distinct()
year = {
"Items": distinctyears
}
return render(request, "home.html", year)
Here is my html :
<div>
{% for item in Items %}
<div class="numbers">{{ item.doj }}</div>
{% endfor %}
</div>
My code is not working. Any help will be highly appreciated.
Thank You!
You need to add .distinct([*fields]) at the end of your query.
Here's the difference. For a normal distinct() call, the database compares each field in each row when determining which rows are distinct. For a distinct() call with specified field names, the database will only compare the specified field names.
As stated all fields in a record are checked. Mostly likely in your case you are getting records with different field values (more likely a case if you are queries multiple tables ManyToMany or ForeignKey relations).

Access column value from relationship property

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.

Reading through joined query Sqlalchemy Jinja

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

Flask - Python WTForms, Populate form with <li> list, <a> anchor data

I am using flask, sqlalchemy, jinja2 and python and I am struggling after getting data from the database and displaying them in my html while they are inside a list and in an anchor to post them with anchor click inside a form to submit them afterwords in another database table.
Here is what I have so far.
My html
<div class="container-fluid">
<div id="training_list">
<h1>List of Exercises</h1>
{% for exercises in exlist %}
<li id="li_ex_list">{{ exercises.name }} | Reputations:
{{ exercises.reps }}</li>
{% endfor %}
</div>
<div id="training_content">
<h1>My Workout Plan</h1>
<form method="POST" action="/my_workouts">
</form>
</br>
<button type="submit" class="btn btn-info">Save my plan</button>
</div>
</div>
My flask-python
class Exercises(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(40), unique=True, nullable=False)
reps = db.Column(db.Integer(), nullable=False)
complete = db.Column(db.Boolean, default=False)
def __init__(self, name, reps, complete):
self.name = name
self.reps = reps
self.complete = complete
def __repr__(self):
return '<Exercises %r>' % self.name
class Traningplan(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
ex_id = db.Column(db.Integer, db.ForeignKey('exercises.id'), nullable=False)
ex_name = db.Column(db.String(40), db.ForeignKey('exercises.name'), nullable=False)
class Usertrainingplan(FlaskForm):
user_id = IntegerField('Userid', render_kw={'readonly': True})
ex_id = IntegerField('Exerciseid', render_kw={'readonly': True})
ex_name = StringField('Exercisename', render_kw={'readonly': True})
#app.route('/my_workouts', methods=['GET', 'POST'])
#login_required
def my_workouts():
exlist = Exercises.query.all()
form = Usertrainingplan()
return render_template('My_Training_Programs.html', exlist=exlist, form=form)
I have tried filtering for complete True/False and passing 2 variables for complete and incomplete(also added them with jinja2 template in my html) but I can't make it work.
---- This part was inside "def_myworkouts():"-----
incomplete = Exercises.query.filter_by(complete=False).all()
complete = Exercises.query.filter_by(complete=True).all()
#app.route('/complete/<id>')
def complete(id):
exercises = Exercises.query.filter_by(id=int(id)).first()
exercises.complete = True
db.session.commit()
return redirect(url_for('my_workouts'))
Additional Information:
Current page looks like this:
current page
So what I want to do is:
Once the user clicks on the links on the left side, I want the text of the links (e.g. Landmine) to be added in the right side of the page where "My workout plan" is, and create a list with all the selected items.
Additionaly I want to be able to place them inside a form and the submit(POST) the form inside a table in my database, with the columns: user_id, ex_id, ex_name as foreign keys. user_id(foreign key) should be the current logged in users id(primary key), ex_id(foreign key) should be the id of the exercise and finally ex_name(foreign key) should be the name of the exercise. The primary keys ofcourse are already commited in the database in the required tables and sames goes with exercises names.
Thanks for your time in advance. Any help highly appriceated.
Stackoverflow you are my only savior

Django - filtering on foreign key

I have a problem about filter in django. Please help me. I want to display the objects of the product which has different categories when I click on l.category_name
my html (CategoryList.html):
{% for l in forms %}
<h2>{{ l.category_name }}</h2>
{% endfor %}
CategoryView.html
{{get_product.product_name}}
my model:
class Category(models.Model):
category_id = models.AutoField(primary_key = True)
category_name = models.CharField(max_length = 20)
def __unicode__(self):
return self.category_name
class Product(models.Model):
product_id = models.AutoField(primary_key = True)
product_name = models.CharField(max_length = 50)
product_category = models.ForeignKey(Category)
product_color = models.CharField(max_length = 30)
def __unicode__(self):
return self.product_name
my view:
def category_list(request):
list = Category.objects.all()
context = {'forms':list}
return render(request,'webpage/CategoryList.html',context)
def category_view(request,category_id):
all = Product.objects.all()
if request.POST:
get_id = Category.objects.get(category_id = request.POST['category_id'])
get_category = Product.objects.get(product_category =
request.POST['product_category'])
get_category.product_category = get_id
get_category.save()
if get_category:
get_product = Product.objects.filter(product_category__category_name =
request.POST['category_name'])
context = {'get_product':get_product}
return render(request,'webpage/CategoryView.html',context)
I read document in https://docs.djangoproject.com/en/1.6/topics/db/queries/ but i don't understand .I know i was wrong category_view
There seem to be a lot of problems with your code.
First, you don't have to declare ids in your code. Django does that automatically for you. So, categor_id and product_id are unnecessary.
Second,
Remove the .POST check. You aren't posting anything.
Third,
get_id = Category.objects.get(category_id = request.POST['category_id']) # returns a category, not an id
get_category = Product.objects.get(product_category =
request.POST['product_category']) # returns the product list, not a category
get_category.product_category = get_id
is the same as
category = Category.objects.get(category_id = request.POST['category_id'])
product_list = Product.objects.get(product_category = category)
Fourth, don't hardcode URLs in your template. Use the {% url %} tag instead.
Finally,
You can then pass this product_list to the template
context = {'product_list':product_list}
return render(request,'webpage/CategoryView.html',context)
The way foreign keys are stored is through automatic fields(IDs). Since 'Category' is a foreign field of 'Product', when you make a record entry, the id of category is stored in 'product_category' field in products table.
I think your code is a little confusing since you are trying to do somethings django does automatically for you. Like, once you define a foreign key, the id of the foreign key table record is stored automatically, you don't have to get the id of 'category' entry and store it in products table entry.
What you are trying to achieve is simple, lets say you have the category_name and nothing else, get the id of the category table entry,
category_object = Category.objects.get(category_name = category_name)
category_id = category_object .id
If you already have the ID of category, then you can skip the above step, and simply use the ID to query the products table to get the needed records
Product.objects.filter(product_category = category_id)
In your templates, you can iterate through these product records and display whatever is needed.
BTW, use the .update() method to update any fields instead of save() method.
Something like this:
Entry.objects.all().update(blog=b)
It will be well worth your time reading through the queries help.
Django queries

Categories