Django Query with 3 tables - python

I'm hoping I can get a little guidance.
I'm trying to return data from 3 related tables in my template. In SQL, this is a simple approach, but the Django requirements have me stumbling.
I'd like to display information similar to this:
WaiverAdult.first_name CheckIn.checkintime
WaiverMinor.first_name CheckIn.checkintime
WaiverAdult.first_name CheckIn.checkintime
WaiverMinor.first_name CheckIn.checkintime
WaiverMinor.first_name CheckIn.checkintime
Here are a simplified representation of the models with the relationships defined.
class WaiverAdult(models.Model):
first_name = models.CharField(max_length=50, blank=True)
class WaiverMinor(models.Model):
first_name = models.CharField(max_length=50, blank=True)
parent = models.ForeignKey(WaiverAdult, on_delete=models.CASCADE)
class CheckIns(models.Model):
adult = models.ForeignKey(WaiverParent, on_delete=models.CASCADE, blank=True, null=True)
minor = models.ForeignKey(WaiverChild, on_delete=models.CASCADE, blank=True, null=True)
checkintime = models.DateTimeField(auto_now_add=True)
Here is my simplified view:
class WaiverListView(ListView):
waiver_adults = WaiverAdult.objects.all().prefetch_related(
'waiverminor_set').order_by('created')
queryset = waiver_adults
context_object_name = "waiver_list"
template_name = 'waiver/waiver_list.html'
And, this is my template.
{% for adult in waiver_list %}
<tr>
<td>{{adult.first_name}}</td>
<td>insert the adult checkin time here</td>
</tr>
{% for child in adult.waiverminor_set.all %}
<tr>
<td>{{child.first_name}}</td>
<td>insert the child checkin time here</td>
</tr>
{% endfor %}
{% endfor %}
I would be very appreciative of details in the explanations as I really want to understand how this all works.

Firstly, for every Foreign key you are creating I suggest you to add a related_name this way you specify the name of reverse relation ship between the children model and parent model in your case for example, your code should be:
class WaiverAdult(models.Model):
first_name = models.CharField(max_length=50, blank=True)
class WaiverMinor(models.Model):
first_name = models.CharField(max_length=50, blank=True)
parent = models.ForeignKey(WaiverAdult, on_delete=models.CASCADE,related_name='minor_of_adult')
and let's explain how does it work, you want to know and get all the minors of some adult, what you should do is specify the adult, and get all minor related to that adult, in code context (try it in django shell, using python manage.py shell):
adult = WaiverAdult.objects.get(first_name='cbirch') #specifying the adult
adult.minor_of_adult.all() #notice how we access the minor using the related_name
same thing apply to the last model CheckIns.

With the models you have created it is possible to have multiple checkin times per parent and child so you need to loop through the list. Also your foreign keys refer to WaiverParent and WaiverChild whilst the actual model names are WaiverAdult and WaiverMinor. You could try the following template:
{% for adult in waiver_list %}
<tr>
<td>{{adult.first_name}}</td>
<td>
{% for checkin in adult.checkins_set.all %}
{{ checkin.checkintime }}
{% endfor %}
</td>
</tr>
{% for child in adult.waiverminor_set.all %}
<tr>
<td>{{child.first_name}}</td>
<td>
{% for checkin in child.parent.checkins_set.all %}
{{ checkin.checkintime }}
{% endfor %}
</td>
</tr>
{% endfor %}
{% endfor %}

Related

Django - how to get data from relationships?

I try to get data over a many-to-many and a one-to-many relationship.
view.py
class PublisherDetailView(generic.DetailView):
model = Publisher
template_name = 'store/publisher_detail_view.html'
models.py
class Publisher(models.Model):
name = models.CharField(null=False, max_length=30)
image = models.ImageField(upload_to='publisher_images/')
class Book(models.Model):
title = models.CharField(null=False, max_length=30)
description = models.CharField(max_length=1000)
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
class reader(models.Model):
name = models.CharField(max_length=40)
book = models.ManyToManyField(Book)
publisher_detail_view.html
{% for reader in publisher.book_set.readers.all %}
<p>{{ reader.name }} </p>
{% endfor %}
I just want to get all readers from a specfic publisher. What is the right code in the template? publisher.book_set.readers.all makes sense to me (early beginner), but doesn't work
book_set.readers doesn't work because book_set is a manager that manages a list of books. To get the readers, you have to iterate through the books:
{% for book in publisher.book_set.all %}
{% for reader in book.reader_set.all %}
<p>{{ reader.name }} </p>
{% endfor %}
{% endfor %}

Apply a filter to a child that is within its parents loop

This is driving me mad (I really have looked everywhere - but I know it must be easy and I'm just being a tad think)
How do I add a filter to a child of a parent (one to many)? The code below provides a list of companies in a table, then on the same page/ table, each company has a list of "charges" which is attributed to that company; thanks to the ForeignKey and using "charge_set" it works great. However, I would like to add a filter to the "charges" for status (so exclude "outstanding" status)
In ROR I would have simply placed the following
<% company.charges.where(status: "Outstanding").each do |charge| %>
AIUI, I can't do this with Python/ Django in the view; so how would I go about adding a simple filter to the child of the parent within this loop?
from django.db import models
class Company(models.Model):
name = models.CharField(max_length=100)
class Charge(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE)
charge_id = models.CharField(max_length=100)
status = models.CharField(max_length=100)
from django.shortcuts import render
from companies.models import Company, Charge, Filing
from django.http import HttpResponse
def watch_list(request):
companies = Company.objects.order_by('-data_date')
return render(request,'company_watch/watch_list.html',{'companies':companies})
{% for company in companies %}
<tr>
<td>{{company.name}}</td>
<td>
<ul>
{% for charge in company.charge_set.all %}
<li>{{charge.charge_id}}</li>
{% endfor %}
</ul>
</td>
</tr>
{% endfor %}
You can just grab the company and filter the children. If you want to the children on the backend:
company = Company.objects.get('...')
outstanding_children = Charge.objects.filter(company = company, status = "Outstanding")
If you want to display the children on the frontend:
{% for company in companies %}
<tr>
<td>{{company.name}}</td>
<td>
<ul>
{% for charge in company.charge_set.all %}
{% if charge.status == "Outstanding" %}
<li>{{charge.charge_id}}</li>
{% endif %}
{% endfor %}
</ul>
</td>
</tr>
{% endfor %}

List of model fields with missing values nicely formatted in Django template

I'm trying to make a view in Django to list each entry in a model and any of the fields that are missing values. The template is a table: one column is a specific field and the other column is a list of the fields with missing values. I have a working version, but the missing fields are just strung together, and I'd like to have them nicely formatted with commas in between.
#models.py
class exif(models.Model):
image = models.ForeignKey('image', on_delete=models.CASCADE, blank=False, null=False)
filename = models.CharField(max_length=100, blank=True, null=True)
artist = models.CharField(max_length=100, blank=True, null=True)
copyright = models.CharField(max_length=100, blank=True, null=True)
keywords = models.CharField(max_length=100, blank=True, null=True)
caption = models.CharField(max_length=100, blank=True, null=True)
comment = models.CharField(max_length=100, blank=True, null=True)
...
#views.py
def exif_export(request):
exif_records = serializers.serialize("python", exif.objects.all())
return render(request, 'exif_export.html', {'exif_records': exif_records})
#exif_export.html
<table>
<tr>
<th>File</th>
<th>Missing Exif Fields</th>
</tr>
{% for record in exif_records %}
<tr>
<td>
{% for field, value in record.fields.items %}
{% if field == 'filename' %}
{{ value }}
{% endif %}
{% endfor %}
</td>
<td>
{% for field, value in record.fields.items %}
{% if not value %}
{{ field }}, <!-- This comma makes a trailing comma at the end of the list -->
{% endif %}
{% endfor %}
</td>
</tr>
{% endfor %}
</table>
Is there a good way to format those fields into a nice list? Right now it'll look like:
artist, comment,
Whereas it'd be nice if it looked like:
artist, comment
Or even better:
Artist, Comment
Do I have to make a dictionary or something in the view and pass that to the template? Or is there a way to make a list of fields missing values in Django and/or Python in general? It'd be nice if the template could just handle it, I just can't think of any other way.
You can do {{ field|capfirst }} for capitalizing the first letter, to remove the trailing comma you could do something like
{% for field, value in record.fields.items %}
{% if not value and field != last_field %}
{{ field|capfirst }},
{% endif %}
{% endfor %}
{{ last_feild|capfirst }}
and pass last_field in somewhere (probably in the model).
Alternatly, you could write a templatetag to do this for you.

django_tables2 ManyToMany Columns

I'm trying to learn Django and web development aspects and have set myself a challenge of writing some 3rd party tools for the popular action RPG, Path of Exile.
A key feature of any ARPG is collecting loot items, which may have any number of 'stats', these are represented in the model as a ManyToManyField. I'd like to list a group of items and their stats in table. I know how to do this with HTML tags in the relevant template.html but would like to use django_tables2 if possible, to reduce repetition etc.
I've played around a bit and read the documentation and the tables.html template but can't see an obvious way of doing this or find any other posts etc, I'd be grateful for any help or a nudge in the right direction.
Here is a mockup of what I'd like the table to look like I'm not too bothered about having cell dividers or not but would like to be able to sort the columns of these many to many aspects.
modely.py
class Stats(models.Model):
name = models.ForeignKey(StatNames, models.DO_NOTHING)
min_value = models.IntegerField()
max_value = models.IntegerField()
class ItemName(models.Model):
name = models.CharField(unique=True, max_length=50)
i_level = models.SmallIntegerField()
min_dmg = models.SmallIntegerField(blank=True, null=True)
max_dmg = models.SmallIntegerField(blank=True, null=True)
stats = models.ManyToManyField(Stats)
tables.py
class ItemTable(tables.Table):
class Meta:
model = poe.models.ItemName
print("ItemName.type", poe.models.ItemName.type)
fields = (
'name',
'i_level',
'stat_name',
'min_value',
'max_value',
)
Here's an example of the html approach I tried out, the variable names are a little different from above but it demonstrates what I had in mind.
<table>
<thead>
<tr>
<th>Name</th>
<th>Something</th>
<th>Something Else</th>
<th colspan='3'>Stats!</th>
</tr>
</thead>
<tbody>
{% for obj in object_list %}
{% with obj.stats.count|default:1 as rowspan %}
<tr>
<td rowspan='{{rowspan}}'>{{obj.name}}</td>
<td rowspan='{{rowspan}}'>{{obj.something}}</td>
<td rowspan='{{rowspan}}'>{{obj.something_else}}</td>
{% for stat in obj.stats.all %}
<td>{{stat.name}}</td>
<td>{{stat.min_value}}</td>
<td>{{stat.max_value}}</td>
{% empty %}
<td colspan='3'>No stats</td>
{% endfor %}
</tr>
{% endwith %}
{% endfor %}
</tbody>
</table>

access to joined models in django views and templates

for joining many models with each other, I did , for example :
Message.objects.filter(conversation__recipient__user=request.user)
when I want to use it in the template side , it doesn't show me anything. example:
{{row.conversation.recipient.user.username}}
this is my code:
model:
class Conversation(models.Model):
user = models.ForeignKey(User)
def __unicode__(self):
return self.user
class Message(models.Model):
conversation = models.ForeignKey(Conversation)
title = models.CharField(max_length=50)
body = models.CharField(max_length=500)
parent = models.IntegerField(default=0)
created = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.title
class Recipient(models.Model):
user = models.ForeignKey(User)
conversation = models.ForeignKey(Conversation)
is_read = models.BooleanField(default=False)
view:
def admin_index(request):
rows = Message.objects.filter(conversation__recipient__user=request.user)
return render(request,'message/admin/index.html',{'rows':rows})
template:
{% for i in rows %}
{% if not i.conversation.recipient.is_read %}
<tr class="set_bold">
{% else %}
<tr>
{% endif %}
<td>name:{{i.conversation.recipient}}</td>
<td class="col-md-0"><input type="checkbox"></td>
<td class="col-md-2">{{i.conversation.user.username}}</td>
<td>{{i.title}}</td>
<td>{{i.created|date:"y-m-d"}} <small>({{i.created|timesince}})</small></td>
</tr>
{% empty %}
<tr>
<td colspan="4">{% trans "dont have any message" %}</td>
</tr>
{% endfor %}
So how can I get access to recipient models in views and templates via Message model?
Thanks
As Recipient model has ForeignKey with Conversation model, there are many recipients for a conversation. So conversation objects will have receipient_set as queryset. You need to iterate over it and get either first/last or all objects to display.
So your template code needs to change as
{% for i in rows %}
{%for recp in i.conversation.recipient_set.all %}
{# do something with each recipient object
{% if not recp.is_read %}
<tr class="set_bold">
{% else %}
<tr>
....
{%endfor%}
{%endfor%}

Categories

Resources