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

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

Related

Django show image list in table from ManyToMany field

I have created a model to have details of job vacancy. The job model has the following fields:
class Job(models.Model):
job_position = models.ForeignKey(Position, on_delete=models.PROTECT, related_name='job_position')
applicants_to_hire = models.IntegerField(null=True, blank=True,
validators=[MinValueValidator(1), MaxValueValidator(15)], default=1)
hiring_team = models.ManyToManyField(Employee, related_name='hiring_team')
class JobListView(LoginRequiredMixin, ListView):
model = Job
template_name = 'recruitment/job_list.html'
context_object_name = 'job_list'
I want to use the hiring_team in a template to show image of each employee (circle avatar), and those images come from the employee model:
class Employee(models.Model):
image = models.ImageField(blank=True, default='blank_profile_picture.jpg')
I've managed to display all images, but they are not in the same table cell, and they add additional table columns:
<tbody>
{% for job in job_list %}
<tr>
<td><span class="employee-table-name">{{ job.job_position }}</span></td>
<td>{{ job.applicants_to_hire }}</td>
{% for team in job.hiring_team.all %}
{% if team.image %}
<td><img src="{{ team.image.url }}" class="rounded-circle img-fluid-80" alt="{{ team }}"></td>
{% endif %}
{% endfor %}
<td>{{ job.job_priority }}</td>
<td>{{ job.job_status }}</td>
<td>{{ job.created|date:"M d, Y" }}</td>
</tr>
{% endfor %}
</tbody>
How can I "concatenate" them to display something like the screenshot below:
This should mean the images should all be under one td block, so just put the td outside of the loop:
<td>
{% for team in job.hiring_team.all %}
{% if team.image %}
<img src="{{ team.image.url }}" class="rounded-circle img-fluid-80" alt="{{ team }}">
{% endif %}
{% endfor %}
<td>

Django 'model' object is not iterable error

I have a problem that I would like you to help me.
I have a list of users that shows fine, but when I try to edit a user I get the error: Django 'Residente' object is not iterable. This is my code:
residente_list.html
<div class="card-body">
{% if not obj %}
<div class="alert alert-info">No hay Residentes</div>
{% else %}
<table class="table table-striped table-hover">
<thead>
<th>Nombres</th>
<th>Genero</th>
<th>T.Documento</th>
<th>N.Documento</th>
<th>Residencia</th>
<th>Estado</th>
<th class="all">Acciones</th>
</thead>
<tbody>
{% for item in obj %}
<tr>
<td>{{ item.nombre_completo }}</td>
<td>{{ item.genero }}</td>
<td>{{ item.t_doc }}</td>
<td>{{ item.numero_doc }}</td>
<td>{{ item.residencia }}</td>
<td>{{ item.estado|yesno:"Activo, Inactivo" }}</td>
<td>
<i class="far fa-edit"></i>
<i class="far fa-trash-alt"></i>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
views.py
class ResidenteEdit(LoginRequiredMixin, generic.UpdateView):
model = Residente
template_name = "res/residente_form.html"
context_object_name = "obj"
form_class = ResidenteForm
success_url = reverse_lazy("res:residentes_list")
login_url = 'bases:login'
success_message = "Residente Editado Satisfactoriamente"
urls.py
path('residentes/edit/<int:pk>', ResidenteEdit.as_view(), name='residentes_edit'),
models.py
class Residente(models.Model):
nombre_completo = models.CharField(
max_length=100,
unique=True)
genero = models.ForeignKey(Genero, on_delete=models.CASCADE)
t_doc = models.ForeignKey(Tdocs, on_delete=models.CASCADE)
numero_doc = models.CharField(max_length=100)
residencia = models.ForeignKey(Predio, on_delete=models.CASCADE)
estado = models.BooleanField(default=True)
Thanks for help
When you are in a list view you are usually dealing with a lot of objects (in your case, a lot of Residente's), but when you are editing them you only have one, therefore, you dont need the
{% for item in obj %}
You can do just
obj.nombre_completo
obj.genero
#...
#etc
This should be done in your residente_form.html file.
I have the suspicion you are trying to run a loop in your residente_form.html. If you still can't solve your problem, try posting your residente_form.html here.

Django need to pivot data from 3 related models

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

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 %}

How to get the fields from object_list in django template

I am using this as Generci view
book_info = {
"queryset" : Book.objects.all(),
"template_name" : "book/book_lists.html",
}
Now in my template book_lists i have
{% for book in object_list %}
<tr>
<td>{{ book.name }}</td>
Is there any way i can loop through all like we have in form
{% for field in form %}
forn.label_tag and field
so that i can use it for all Models
so basically i want something like
{% for obj in object_list %}
<tr>
{% for fields in obj %}
<td> {{field.name}}:{{field}} </td>
You need to create a get_fields function in your model(s) which you can then use in the templates to access the fields and values generically. Here is a complete example.
books/models.py:
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=30)
author = models.CharField(max_length=30)
isbn = models.IntegerField(max_length=20)
published = models.BooleanField()
def get_fields_and_values(self):
return [(field, field.value_to_string(self)) for field in Book._meta.fields]
templates/books/book_list.html:
<table>
{% for obj in object_list %}
<tr>
{% for fld, val in obj.get_fields_and_values %}
<td>{{ fld.name }} : {{ val }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>