Access column value from relationship property - python

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.

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

Possible to delete an object's specific row in django?

I have model that creates several different rows. Like this...
class Myteam(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
QB = models.CharField(max_length=80, null=True)
RB1 = models.CharField(max_length=80, null=True)
WR = models.CharField(max_length=80, null=True)
TE = models.CharField(max_length=80, null=True)
D = models.CharField(max_length=80, null=True)
K = models.CharField(max_length=80, null=True)
In my template, I'm looping through the rows and displaying them as html table rows. I've added a delete function to each row because I'd like the app's user to be able delete a player from their "Myteam" object. Problem is, I can only delete the entire object because there's only one id which is for the entire object. The template looks like this right now. Here's two rows as an example.
<tbody>
{% for t in team %}
<tr>
<td id="" > {{ t.QB }}
Delete
</td>
<tr>
<td> {{ t.RB1 }}
Delete
</td>
</tr>
<tr>
I did come across one possible solution here which suggested using {{ forloop.counter }} to assign individual id's to the row, but I can't get this to work how I would like.
Just in case, here's the views.py
def delete_player(request, id):
player = Myteam.objects.get(id=id)
player.delete()
return redirect('show')
I feel like there's probably a pretty basic solution to this but I'm new to django and am having trouble finding such answers. Any help is greatly appreciated.
Ok, so those model fields are database table columns, not rows. Instances of the model are table rows.
You don't delete columns, instead you set them to null. Your view could for example take the column name as a string argument:
def delete_player(request, id, col_name):
player = Myteam.objects.get(id=id)
setattr(player, col_name, None)
player.save()
return redirect('show')
Also since your fields are CharFields, it's best practice to not make them nullable (meaning, don't set null=True), but instead use an empty string "" to indicate an unset value. This way you don't have two different values that indicate emptyness. Thus in the view you would use setattr(player, col_name, "").

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

Django Query Set to get Row value as column name

These are my models:
class Build(models.Model):
name = models.CharField(db_index=True, max_length=56)
description = models.TextField(max_length=512, null=True, blank=True)
class Case(models.Model):
name = models.CharField(db_index=True, max_length=255)
description = models.TextField(max_length=1024, null=True, blank=True)
class Result(models.Model):
status = models.CharField(max_length=12)
result_build = models.ForeignKey(Build, related_name='result_build', on_delete=models.CASCADE)
result_case = models.ForeignKey(Case, related_name='result_case', on_delete=models.CASCADE)
I need a django QuerySet to get data like below:
....................................................
: case_name : case_description : build_X : build_Y :
:...........:..................:.........:.........:
: test1 : case1 : PASS : FAIL :
: test2 : case2 : FAIL : PASS :
:...........:..................:.........:.........:
where case_name and case_description are fields from Case model.
build_X and build_Y are the two build names available in Build model
and PASS and FAIL are the status for different cases and builds from Result model.
This is a classical case for many-to-many relationship with a through model. The model classes can be refactored as following:
class Build(models.Model):
name = models.CharField(db_index=True, max_length=56)
description = models.TextField(max_length=512, null=True, blank=True)
class Case(models.Model):
name = models.CharField(db_index=True, max_length=255)
description = models.TextField(max_length=1024, null=True, blank=True)
builds = models.ManyToManyField('Build', through='Result', related_name='cases')
class Result(models.Model):
status = models.CharField(max_length=12)
build = models.ForeignKey('Build', on_delete=models.CASCADE)
case = models.ForeignKey('Case', on_delete=models.CASCADE)
We can also call the properties of Result simply build and case, without redundancy in the names. Although related_name doesn't bother, we don't really need it here.
Now you can use the m2m relationship to query your models:
case = Case.objects.get(pk=1) # get Case object with primary key 1
case.builds.all() # get all Build objects related to case
build = Build.objects.get(pk=1) # get Build object with primary key 1
build.cases.all() # get all Case objects related to build
# UPDATE
# get Result objects for the "case" with pk=1 retrieved before
results = Result.objects.filter(case=case)
# output each entry as row with all property values
for r in results:
print(r.case.name, r.case.description, r.build.name, r.build, description, r.status)
You can also use .filter to narrow down your query result.
EDIT:
Here is one possible way to create a matrix table. This is a code you can put in your view:
cases = Case.objects.all()
builds = Build.objects.all()
matrix = []
for c in cases:
result = {}
result['case'] = c
result['status'] = []
for b in builds:
result['status'].append(Result.objects.filter(
case=c,
build=b
).values_list('status', flat=True))
matrix.append(result)
Now you should have a table with a dictionary for every case. Pass builds and matrix as context to your template. Then you can iterate over the builds to create the table header (take care to leave space for a column or two in the beginning for listing the cases). Then iterate over the matrix and create the table body. First get the case for the first column (or the first two columns) and then output the status.
I hope this can point you the way. Once you have the right result you can further optimize the performance.
EDIT 2:
Here is an example how the table could look like.
Pass builds and matrix from the code snippet above as context to the template:
<table>
<tr>
<th>Case name</th>
<th>Case description</th>
{% for b in builds %}
<th>{{ b.name }}</th>
{% endfor %}
</tr>
{% for row in matrix %}
<tr>
<td>{{ row.case.name }}</td>
<td>{{ row.case.description }}</td>
{% for s in row.status %}
<td>{{ s.0 }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
In the first for loop we create a table header with two column headers for case name and case description and one column header for each build.
In the second for loop we create a table row for each case. In the nested loop we output the status.
It is a very simple approach and probably can be further optimized, but I leave that to you.

SQLAlchemy query tables joined with foreign key

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

Categories