Django need to pivot data from 3 related models - django

First of all, excuse my English, I'm new in Django, and really eager to learn. I've been doing things for one year with Python. That's all my experience.
I'm trying to make a table with a list of articles listed in a column of the table, and in the other columns there is the name of some sellers at the top, then the price of the articles is listed in each sellers column with their respective price of that article.
I have this code in models.py
class Item(models.Model):
name = models.CharField(max_length=200)
class Seller(models.Model):
name = models.CharField(max_length=200)
class Price(models.Model):
item = models.ForeignKey(Item, on_delete=models.CASCADE)
seller = models.ForeignKey(Seller, on_delete=models.CASCADE)
price = models.CharField(max_length=10)
This is my views.py
def index(request):
all_items = Item.objects.all()
sellers = Seller.objects.all()
prices = Price.objects.all()
return render(request, 'juegos/index.html',
{'all_items': all_items,
'sellers': sellers,
'prices': prices}
)
In the template i have this:
<table>
<thead>
<tr>
<th>Item</th>
{% for seller in sellers %}
<th>{{ seller.name }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for item in all_items %}
<tr>
<td>{{ item.name }}</td>
<!--I don't know how to list each price for each seller-->
{% endfor %}
</tr>
</tbody>
</table>
This is an example of how the information should be displayed:
When I add prices in the admin, I just choose item from a previous added item, and choose seller from a list of seller i added before.
So it's like everything is related, but I can't find the way to relate this objects when making this table.
I tried many things, but I started to feel like I was trying random things, so here I am.
Any hint will be appreciated.
Edit: I've been thinking that a solution could be to iterate sellers name in the table header, and then in the table body iterate each price of each item, following the order from the header interation, but i don't know what function should i use to do this. The
<table>
<thead>
<tr>
<th>Item</th>
{% for seller in sellers %}
<th>{{ seller.name }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for item in all_items %}
<tr>
<td>{{ item.name }}</td>
<!-- It would be cool if i could iterate trought sellers and just put the price like this:-->
{% for seller in sellers %}
{{ price }}
{% endfor %}
{% endfor %}
</tr>
</tbody>
</table>

Try this
{% for item in all_items %}
<tr>
<td>{{ item.name }}</td>
</tr>
{% for row in item.price_set.all %}
<tr>
<td>{{ row.price }}</td>
</tr>
{% endfor %}
{% endfor %}

You only need price
{% for p in price %}
{{ p.item.neme }}
{{ p.seller.name }}
{{ p.price }} EUR
{% endfor %}
view.py
def index(request):
prices = Price.objects.all()
return render(request, 'juegos/index.html',
{'prices': prices}
)

Related

Outputting "Following relationships 'backward'" to a template

I started working with Django earlier this week and watched a few tutorials and thought I would try out switching over a site I made in PHP to something a little more current. I ran into some issues over the weekend and managed to get help with some of them, but one issue remains.
I already have an existing database with some information it that I want to retrieve to display on a website. The information is modified through the database so Django doesn't have to deal with any of that - just displaying results to a bootstrapped template file.
So, in essence Django would have 2 models - one for each relevant database table.
The first model relates to products
class Bikes(models.Model):
bikempn = models.CharField(primary_key=True, max_length=50)
bikecategory = models.CharField(max_length=50)
bikeyear = models.CharField(max_length=4)
bikebrand = models.CharField(max_length=50)
bikedesc = models.CharField(max_length=255)
bikesize = models.CharField(max_length=50)
bikecolour = models.CharField(max_length=255)
bikeurl = models.CharField(max_length=255)
class Meta:
managed = False
db_table = 'bikes'
The second model relates to their expected arrival dates. This model would represent a database view which performs some logic comparing product that is on its way to the number of matching items that are on reserve.
The model is:
class Eta(models.Model):
bikempn = models.ForeignKey(Bikes, on_delete=models.CASCADE, primary_key = True, db_column='bikempn', related_name='etas')
eta = models.DateField()
class Meta:
managed = False
db_table = 'bike_eta'
The idea of this website is essentially to let people know if a product they are interested in will be arriving any time soon.
I have been trying to come up with a query that will display all of the linked information, but everything I find online hasn't quite worked. I have received some help along the way but have hit the wall again and frankly feel foolish having not figured this out yet.
So, currently I have a query that is:
kidsquery = Bikes.objects.filter(bikecategory='kids').select_related('etas')
This filters out only bikes categorized as kids bikes and should join the two models together.
My relevant template to display this information is:
{% extends 'base.html' %}
{% block content %}
{% if kidsquery %}
<div class="table-responsive">
<table class="table table-striped table-hover table-bordered table-sm">
<thead class="table-dark">
<tr>
<th scope="col">Year</th>
<th scope="col">Brand</th>
<th scope="col">Model</th>
<th scope="col">Colour</th>
<th scope="col">Size</th>
<th scope="col">Part#</th>
<th scope="col">ETA</th>
</tr>
</thead>
{% for info in kidsquery %}
<tr>
<td>{{ info.bikeyear }}</td>
<td>{{ info.bikebrand }}</td>
{% if info.bikeurl %}
<td>{{ info.bikedesc }}</td>
{% else %}
<td>{{ info.bikedesc }}</td>
{% endif %}
<td>{{ info.bikecolour }}</td>
<td>{{ info.bikesize }}</td>
<td>{{ info.bikempn }}</td>
{% for arrival in info.etas.all %}
{% if arrival is null %}
<td>Contact Us</td>
{% else %}
<td>{{ arrival|date:"F Y" }}</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</table>
</div>
{% endif %}
{% endblock %}
Does anyone know why I cannot get anything to display in the final column (where it should display the eta date values (or failing that, the "Contact Us" result)?
The proper solution to pulling the information out in the template:
{% extends 'base.html' %}
{% block content %}
{% if kidsquery %}
<div class="table-responsive">
<table class="table table-striped table-hover table-bordered table-sm">
<thead class="table-dark">
<tr>
<th scope="col">Year</th>
<th scope="col">Brand</th>
<th scope="col">Model</th>
<th scope="col">Colour</th>
<th scope="col">Size</th>
<th scope="col">Part#</th>
<th scope="col">ETA</th>
</tr>
</thead>
{% for info in kidsquery %}
<tr>
<td>{{ info.bikeyear }}</td>
<td>{{ info.bikebrand }}</td>
{% if info.bikeurl %}
<td>{{ info.bikedesc }}</td>
{% else %}
<td>{{ info.bikedesc }}</td>
{% endif %}
<td>{{ info.bikecolour }}</td>
<td>{{ info.bikesize }}</td>
<td>{{ info.bikempn }}</td>
{% for arrival in info.etas.all %}
{% if arrival.eta %}
<td>{{ arrival.eta|date:"F Y" }} </td>
{% else %}
<td>Contact Us</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</table>
</div>
{% endif %}
{% endblock %}
Swapped around the checks, and compensated for the lack of a date.

Outputting a table to a template

I recently began to study Django and cannot understand how to correctly implement the template (table)
I have a model:
She has ties to Company and bank.
class BankAccount(models.Model):
company = models.ForeignKey('Company.Company', null=True, on_delete=models.CASCADE, verbose_name='Фирма',
related_name='company')
bank = models.ForeignKey('Bank', null=True, on_delete=models.CASCADE, verbose_name='Банк', related_name='bank')
login_bank = models.CharField(max_length=255, verbose_name='Логин', null=False)
password_bank = models.CharField(max_length=255, verbose_name='Пароль', null=False)
date_created = models.DateTimeField(auto_now=True)
date_updated = models.DateTimeField(auto_now_add=True)
balance = models.DecimalField(max_digits=15, decimal_places=2, blank=True, null=True)
In the template I want to display a table where all firms / banks will be displayed at the intersection of these columns and rows, a value from this model will appear.
I've been looking for an answer for 5 days and
Company1
Company2
Company3
Company4
Bank1
Balance(Company1/Bank1)
Balance(Company2/Bank1)
Balance(Company/Bank1)
Balance(Company4/Bank1)
Bank2
Balance(Company1/Bank2)
....
....
....
Bank3
Balance(Company1/Bank3)
....
....
....
I tried the example template below, thanks
#Linh Nguyen
<table class="table table-bordered">
<tr>
<th> </th>
{% for company in companies %}
<th>{{ company.name }}</th>
{% endfor %}
</tr>
{% for bank in banks %}
<tr>
<td>{{bank.name}} </td>
{% for company in companies %}
{% for account in bank_accounts %}
{% if account.company_id == company.id and bank.id == account.bank_id %}
<td>{{account}}</td>
{% endif %}
{% endfor %}
{% endfor %}
</tr>
{% endfor %}
</table>
context = {
'companies': Company.objects.all().order_by('id'),
'banks': Bank.objects.all().order_by('id'),
'bank_accounts': BankAccount.objects.all()
}
But the result is still not desired. One firm can have 2 and 4 accounts in the table of accounts (bank_account).
and if the company does not have all 4 accounts, the data no longer match.If you add {% else%}, then the table breaks completely.
screen
<div class="col-md-12">
<div class="row">
<table class="table table-bordered">
<tr>
<th> </th>
{% for company in companies %}
<th>{{ company.name }}</th>
{% endfor %}
</tr>
{% for bank in banks %}
<tr>
<td>{{bank.name}} </td>
{% for company in companies %}
<td>
{% for account in bank_accounts %}
{% if account.company_id == company.id and bank.id == account.bank_id%}
{{account}}
{% endif %}
{% endfor %}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
</div>
</div>
Finally it worked out!
I think it only come down to how you handle the logic in the looping:
in your view you have the querysets for all the banks, companies and bank accounts
view.py:
context = {
'companies': Company.objects.all(),
'banks': Bank.objects.all(),
'bank_accounts': BankAccount.objects.all()
}
template.html:
<table style="width:100%">
<tr>
{% for company in companies %}
<th>{{ company.name }}</th>
{% endfor %}
</tr>
{% for bank in banks %}
<tr>
{% for company in companies %}
{% for bank_account in bank_accounts %}
<td>
{% if bank_account.company == company %}
Balance({{ company.name }}/{{ bank.name }})
{% endif %}
</td>
{% endfor %}
{% endfor %}
</tr>
{% endfor %}
</table>
First, you should implement your view function like this:
from django.shortcuts import render
from models import BackAccount
def get_bank_accounts(request):
accounts = BankAccount.objects.all()
context = {
'accounts': accounts,
}
return render(request, 'accounts.html', context)
So, that way you send all accounts to the template. And the way you use them in accounts.html like this:
{% for account in accounts %}
{{ account.bank }}
{{ account.company }}
{% endfor %}

Displaying data from intermediate table in a many-to-many relation

I have 3 models that follow the principle described in Django documentation (https://docs.djangoproject.com/en/2.2/topics/db/models/#extra-fields-on-many-to-many-relationships):
class Topic(models.Model):
key = models.CharField(max_length=255, primary_key=True)
persons = models.ManyToManyField('Person', through='Interest')
class Person(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
interests = models.ManyToManyField('Topic', through='Interest')
class Interest(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
source = models.ForeignKey(TopicSource, on_delete=models.CASCADE)
The views.pyis really simple:
class TopicsView(generic.ListView):
template_name = 'app/topics.html'
context_object_name = 'topics_list'
def get_queryset(self):
return Topic.objects.all()
The template is actually giving me headaches:
<table>
<tbody class="list">
{% for item in topics_list %}
<tr>
<td>{{ item.key }}</td>
<td>{{ item.person_set.all.count }}</td>
<td>
<ul>
{% for person in item.person_set.all %}
<li>{{ person.last_name }}, {{ person.first_name }} [{% person.interests_set.get(cluster=item) %}]</li>{% endfor %}
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
With {% person.interests_set.get(topic=item) %}, I'm trying to access data from the intermediate table.
How can I display the source of the interest next to the name of the person?
This solution is giving hint on how to do this from the shell but I cannot figure out how to achieve that in the template.
Django templates don't support what you're trying to do out of the box. You would have to create a custom tag to implement what you're trying.
However you could do:
class TopicsView(generic.ListView):
template_name = 'app/topics.html'
context_object_name = 'topics_list'
queryset = Topic.objects.prefetch_related(
Prefetch(
'interest_set',
Interest.objects.select_related('person')
)
Then adjust your template to iterate over the interests.
<table>
<tbody class="list">
{% for item in topics_list %}
<tr>
<td>{{ item.key }}</td>
<td>{{ item.interest_set.all.count }}</td>
<td>
<ul>
{% for interest in item.interest_set.all %}
<li>{{ interest.person.last_name }}, {{ interest.person.first_name }} [ {{ interest.source }} ]</li>{% endfor %}
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
As an alternative to the solution proposed by #schillingt (the accepted solution), I came up with the below solution:
views.py:
class InterestsView(generic.ListView):
template_name = 'app/interests.html'
context_object_name = 'interests_list'
def get_queryset(self):
return Interest.objects.order_by('topic', 'person').all()
template:
{% regroup interests_list by topic as l1%}
<table>
<tbody>
{% for topic in l1 %}
<tr>
<td>{{ topic.grouper }}</td>
<td>{{ topic.list|length }}</td>
<td>
<ul>
{% for item in topic.list%}
<li>{{ item.person.last_name }}, {{ item.person.first_name }} [{{ item.source }}]</li>
{% endfor %}
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
However, the solution from #schillingt is better since it's performance are a lot better (by a factor 2),

Django Master/Detail Template

I have a two classes that define two models
class Master(models.Model):
date = models.DateTimeField()
status = models.CharField(default = 'R')
class Detail(models.Model):
name = models.TextField()
from = models.ForeignKey(Master)
The view:
def list_view(request):
masters = master.objects.filter()
context = {'masters': masters}
return render_to_response('list.html', context, context_instance = RequestContext(request))
The template:
{% for master in masters %}
<tr>
<td>{{ master.date }}</td>
<td>{{ master.status }}</td>
<td>{# THIS #}</td>
</tr>
{% endfor %}
I want to show detail names in {# THIS #} section, but I don't know how to modify the View for that. How can I access to details for the master in template?
You can do something like this to get the names
{% for master in masters %}
<tr>
<td>{{ master.date }}</td>
<td>{{ master.status }}</td>
<td>
{% for detail in master.detail_set.all %}
{{detail.name}}<br/>
{% endfor %}
</td>
</tr>
{% endfor %}

Counting objects and showing the results in Django

I have a question regarding counting objects, filtering the results and finally putting them into my template.
in Models.py:
class Fms(models.Model):
department = models.CharField(max_length=255, verbose_name='Department')
date = models.DateField()
in Views.py:
def raport(request):
raport = Fms.objects.filter(date__year='2013').extra(select={'month': "EXTRACT(month FROM date)"}).values('month', 'department').annotate(Count('department'))
return render_to_response ('Fms/raport.html',
{'raport': raport},
context_instance=RequestContext(request))
Question is, how to show a result in a table like:
Or at least make it show each month and how many times department was mentioned in that month.
you need something like:
{% for each_model in raport %}
{{each_model.department}}
{{each_model.date}}
{% endfor %}
If you do not pass a keyword to annotate, it uses the name of the field with __count appended to it. https://docs.djangoproject.com/en/dev/topics/db/aggregation/#generating-aggregates-for-each-item-in-a-queryset To access that in your template you would simply do:
<table>
<thead>
<tr>
<th>Month</th>
<th>Count</th>
</tr>
</thead>
<tbody>
{% for item in raport %}
<tr>
<td>{{ item.month }}</td>
<td>{{ item.department__count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
To display it horizontally...
<table>
<thead>
<tr>
{% for item in raport %}
<th>{{ item.month }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
<tr>
{% for item in raport %}
<td>{{ item.department__count }}</td>
{% endfor %}
</tr>
</tbody>
</table>