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.
Related
I have two models interrelated items and broken :
class Items(models.Model):
id = models.AutoField(primary_key=True)
item_name = models.CharField(max_length=50, blank=False)
item_price = models.IntegerField(blank=True)
item_quantity_received = models.IntegerField(blank=False)
item_quantity_available = models.IntegerField(blank=True)
item_purchased_date = models.DateField(auto_now_add=True, blank=False)
item_units = models.CharField(max_length=50, blank=False)
def __str__(self):
return self.item_name
class Broken(models.Model):
item = models.ForeignKey(Items, default=1, on_delete=models.CASCADE)
item_quantity_broken = models.IntegerField(blank=True)
item_broken_date = models.DateField(auto_now_add=True, blank=False)
item_is_broken = models.BooleanField(default=True)
date_repaired = models.DateField(auto_now=True, blank=True)
def __str__(self):
return self.item.item_name
I wrote this view function to retrieve data to a table into a template:
def broken_items(request):
br = Broken.objects.select_related('item').all()
print(br.values_list())
context = {
'title': 'broken',
'items': br,
}
return render(request, 'store/broken.html', context)
this is the executing query:
SELECT "store_broken"."id",
"store_broken"."item_id",
"store_broken"."item_quantity_broken",
"store_broken"."item_broken_date",
"store_broken"."item_is_broken",
"store_broken"."date_repaired",
"store_items"."id",
"store_items"."item_name",
"store_items"."item_price",
"store_items"."item_quantity_received",
"store_items"."item_quantity_available",
"store_items"."item_purchased_date",
"store_items"."item_units"
FROM "store_broken"
INNER JOIN "store_items"
ON ("store_broken"."item_id" = "store_items"."id")
looks like it gives me all the fields I want. In debugger it shows data from both tables,
so I wrote for loop in template,
{% for item in items %}
<tr>
<td>{{item.id}}</td>
<td>{{item.item_id}}</td>
<td>{{item.item_quantity_broken}}</td>
<td>{{item.item_broken_date}}</td>
<td>{{item.item_is_broken}}</td>
<td>{{item.date_repaired}}</td>
<td>{{item.item_name }}</td>
<td>{{item.item_item_quantity_received}}</td>
<td>{{item.item_quantity_available}}</td>
<td>{{item.item_purchased_date}}</td>
<td>{{item.items_item_units}}</td>
</tr>
{% endfor %}
The thing is this loop only gives me data from broken table only. I can't see data from Items table.
can someone help me to find the reason why other details are not showing?
Your items query is of Broken objects. So in order to access the Items values you need to change your table. For better understanding change your view like this:
brokens = Broken.objects.select_related('item').all()
context = {
'title': 'broken',
'brokens ': brokens,
}
and then your table:
{% for broken in brokens %}
<tr>
<td>{{broken.id}}</td>
<td>{{broken.item.pk}}</td> # This is the item id
<td>{{broken.item_quantity_broken}}</td>
<td>{{broken.item_broken_date}}</td>
<td>{{broken.item_is_broken}}</td>
<td>{{broken.date_repaired}}</td>
<td>{{broken.item.item_name}}</td>
<td>{{broken.item.item_quantity_received }}</td>
<td>{{broken.item.item_quantity_available}}</td>
<td>{{broken.item.item_purchased_date}}</td>
<td>{{broken.item.items_item_units}}</td>
</tr>
{% endfor %}
you loop over a List of Broken objects
to access the related item objects
item.item.item_name
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).
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, "").
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