Django -Help displaying content in nested for loop - django

I have a Requisition which may have multiple lines. So to implement this I included a unique_together constraint on two fields on the RequisitionLine model which one of the fields is a FK to Requisition . So naturally to pull all the lines of a requisition I would query the RequisitionLine table where the FK = the id of the Requisition model, then iterate over all the sequences to grab all the lines.
My goal is to display the header number with the lines of that requisition under the header in the template but am struggling to accomplish this. I have tried to iterate over the queryset but the code i am posting below is my most recent attempt trying pass lists to the template which is also not working. Right now each header is showing all the lines. Any help would be appreciated.
Models.py
class Requisition(models.Model):
username = models.ForeignKey(
'users.CustomUser', on_delete=models.CASCADE, related_name='req_user')
signature = models.CharField(max_length=10, blank=True, null=True)
class RequisitionLine(models.Model):
parent_req = models.ForeignKey('Requisition', on_delete=models.CASCADE, related_name='par_req_line' )
sequence = models.PositiveIntegerField()
item_code = models.ForeignKey(
'items.ItemMaster', on_delete=models.CASCADE, related_name='req_item', blank=True, null=True)
description = models.CharField(max_length=50, blank=True)
extra_information = models.TextField(blank=True)
quantity = models.PositiveIntegerField(blank=True, default=0,null=True)
price = models.DecimalField(max_digits=19, decimal_places=2, blank=True, default=0.00,null=True)
purchase_order = models.CharField(max_length=9, blank=True,null=True)
po_line = models.PositiveSmallIntegerField(blank=True,null=True)
req_delivery_date = models.DateField(blank=True,null=True)
act_delivar_date = models.DateField(blank=True, null=True)
status = models.ForeignKey('RequisitionStatus', related_name='req_status', on_delete=models.CASCADE, blank=True, null=True)
assistance = models.ForeignKey('users.UserRoles', related_name='req_assist', blank=True, null=True, on_delete=models.CASCADE, limit_choices_to= ~Q(role='Requestor'))
catagory = models.ForeignKey('items.ItemCatagory', on_delete=models.CASCADE, related_name='line_cat', blank=True, null=True)
notes = models.TextField(blank=True)
class Meta:
unique_together = ('parent_req','sequence')
Views.py
def pending_action(request):
user = CustomUser.objects.get(username=request.user)
user_req_headers = Requisition.objects.filter(username=user)
complete_status = RequisitionStatus.objects.get(status='Completed')
req_line_list = []
for req_header in user_req_headers:
req_lines = RequisitionLine.objects.filter(Q(parent_req = req_header) & ~Q(status=complete_status))
req_line_list.append(req_lines)
return render(request, 'req/pending_action.html', {'user_req_headers':user_req_headers,'req_line_list':req_line_list})
pending_action.html
{% for header in user_req_headers %}
<h3>{{header}}</h3>
{% for req_line in req_line_list %}
{% for req in req_line %}
<table>
{% if forloop.first %}
<tr>
<th>description</th>
<th>catagory</th>
</tr>
{% endif %}
<tr>
<!-- FOR LOOP HERE TO ITERATE OF LIST OF QUERYSETS-->
<td>{{ req }}</td>
</tr>
</table>
{% endfor %}
{% endfor %}
{% endfor %}

I solved this problem by creating a queryset of the header and using a reverse relationship to capture the lines associated with each header
<table class="table">
{% for req_header in req_header_list %}
<tr>
<ul>{{ req_header }}
{% for line in req_header.par_req_line.all %}
<li>{{line}}</li>
{% endfor %}
</ul>
</tr>
{% endfor %}
</table>

Related

Create a string that contains values from related objects to display in view

I'm new to Django so please bear with me. I'm trying to create a view which lists JournalEntries which have type=BP. The list needs to include selected values from the LineItems that are related to each JournalEntry. Each JournalEntry with type=BP only ever has two related LineItems.
models.py
class JournalEntry(models.Model):
date = models.DateField(null=True, blank=False)
TYPE = (
('BP', 'Bank Payment'),
('YE', 'Year End'),
)
type = models.CharField(
max_length=2,
choices=TYPE,
blank=True,
default='0'
)
class LineItem(models.Model):
journal_entry = models.ForeignKey(JournalEntry, on_delete=models.PROTECT)
ledger = models.ForeignKey(Ledger, on_delete=models.PROTECT)
description = models.CharField(max_length=255, null=True, blank=True)
cr = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=True)
dr = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=True)
class Ledger(models.Model):
name = models.CharField(max_length=255)
views.py
def journalentries_show_all(request):
journal_entries = JournalEntry.objects.filter(type="BP")
context = {
'journal_entries': journal_entries,
}
return render(request, 'journal/journalentries_show_all.html', context)
My template journalentries_show_all.html
<ul>
{% for journal_entry in journal_entries %}
<li>{{ journal_entry.date }}</li>
<li>{{ ledger name from first line item in this journal_entry }}</li>
<li>{{ ledger name from second line item in this journal_entry }}</li>
<li>{{ description from first line item in this journal_entry }}</li>
<li>{{ description from second line item in this journal_entry }}</li>
{% endfor %}
</ul>
In this particular view I'm only interested in displaying JournalEntries which has type=BP, all of which only ever have two line items. Other types of JournalEntries have more LineItems, but those are dealt with in another view.
Basic Python iteration is enough for what your need
results = []
for entry in journal_entries:
results.append(entry.lineitem_set.all()[2]
Just that results list should do it in the context. .all()[2] will send a query with a limit for the first 2 items. You will get the whole items. Select what you need to display in the template, not the views.
To make your life easier, you can also define a related_name attribute on foreign keys and use the name you want there instead of the generated *_set
Why do you want to pass the information as a string? It's an extra step that doesn't add anything. You can pass in entire dictionaries as the context variable so that's what I'd recommend you do. In your view, it is possible to request rows based off of a filter as seen in the docs here. Then you can pass the data from the query into the template as a dictionary.
Also why does your JournalEntry model only have one field? Can we just combine the JournalEntry and LineItem into something like this:
class LineItem(models.Model):
date = models.DateField(null=True, blank=False)
# journal_entry = models.ForeignKey(JournalEntry, on_delete=models.PROTECT)
# this journal_entry fk wouldn't work so you'd have to replace it with something else
ledger = models.ForeignKey(Ledger, on_delete=models.PROTECT)
description = models.CharField(max_length=255, null=True, blank=True)
cr = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=True)
dr = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=True)
Then for the view I would write something like this:
views.py
from .models import LineItem
def journalentries_show_all(request):
context = LineItem.objects.filter(arbitrary_criteria = True) # choose your own criteria here
return render(request, 'journal/journalentries_show_all.html', context)
The important part would be choosing the filter. If you want to do the first two by date you can follow the example here.
Maybe I understood you wrong but why don't you just send your JournalEntry querset to your template?
def journalentries_show_all(request):
context['journal_entries'] = JournalEntry.objects.all()
....
Then you can display what you want in your template
{% for entry in journal_entries %}
{{ entry.id }} {{ entry.date }}
{% for item in entry.lineitem_set.all|slice:"2" %}
{{ item.ledger.name }}
{% endfor %}
{% endfor %}
After some digging I found that I can access related variables direct in the template as follows:
<td>{{ journal_entry.date }}</td>
<td>{{ journal_entry.lineitem_set.all.1.ledger }}</td>
<td>{{ journal_entry.lineitem_set.all.0.ledger }}</td>
<td>{{ journal_entry.lineitem_set.all.1.description|truncatechars:15 }}</td>
<td>{{ journal_entry.lineitem_set.all.0.description|truncatechars:15 }}</td>

foriegn key object not iterable in template

I have basic models for a section, subsection and clause. 1 section can hold multiple subsections. Each subsection can hold multiple clauses. The models look like:
**models.py**
class Clause(models.Model):
number = models.CharField(max_length=8, unique=True)
requirements = models.TextField(max_length=2000, unique=False, blank=True, null=True)
documentation = models.TextField(max_length=2000, unique=False, blank=True, null=True)
class Subsection(models.Model):
number = models.CharField(max_length=5, unique=True)
name = models.CharField(max_length=150, unique=False)
descriptor = models.TextField(max_length=2000, unique=False, blank=True, null=True)
clause = models.ForeignKey(Clause, on_delete=models.DO_NOTHING, related_name="clause")
class Section(models.Model):
number = models.CharField(max_length=2, unique=True)
name = models.CharField(max_length=150, unique=False)
descriptor = models.TextField(max_length=2000, unique=False, blank=True, null=True)
subsection = models.ForeignKey(Subsection, on_delete=models.DO_NOTHING, related_name="subsection")
basic view function to call the desired section:
**views.py**
def main(request):
form = MainSearchForm()
user = request.user
sections = []
show_results = True
if 'query' in request.GET:
show_results = True
query = request.GET['query'].strip()
if len(query) <= 2:
sections = Section.objects.filter(number__iexact=query)
if sections:
records = sections
tpl = "display_console.html"
context = {'user': user, 'records': records, 'form': form}
return render(request, tpl, context)
else:
tpl = "main.html"
context = {'user': user, 'form': form}
return render(request, tpl, context)
unfortunately, I can't get my template to return my subsection data. The following returns a 'Subsection' object is not iterable error:
**template**
<table class="section_tb">
{% if records %}
{% for record in records %}
<tr>
<td>{{ record.number }}</td><td>{{ record.name }}</td>
</tr>
{% for item in record.subsection %}
<tr>
<td>{{ item.number }}</td><td>{{ item.name }}</td>
</tr>
<tr>
<td colspan=2>{{ item.descriptor }}</td>
</tr>
{% endfor %}
</table>
{% endfor %}
{% else %}
{% endif %}
substituting:
{% for item in record.subsection.all %}
for:
{% for item in record.subsection %}
removes the error message, but doesn't return any data. Is there something obvious I'm doing wrong?
This is because Section can have only one Subsection.
So you can access the subsection with just {{record.subsection}} so no forloop needed here.
As a tip remember when you usea one to many, the one is where the foreign key is defined.
The model that store the foreign key will always store only one foreign key.
If you want to access the many foreign keys from the other side use the model_name_in_lowercase_set or define a related name in models.ForeignKey(..., related_name="something") then you can call something_set

Filtering date within daterange and show data of one template to another template in django

I have created many sitting dates by using following Sitting model:
class Sitting(models.Model):
sit_date = models.DateField(blank=False,unique=True)
cut_off_date = models.DateField(null=True, blank=True)
ballot_date = models.DateField(null=True, blank=True)
sess_no = models.ForeignKey(Session,
on_delete=models.CASCADE)
genre = TreeForeignKey('Genre', null=True, blank=True, db_index=True, unique=True)
Output of sitting template as following format:
I also designed another model named Circular:
class Circular(models.Model):
cir_no = models.IntegerField(blank=False)
sit_start_date = models.DateField(blank=False)
sit_end_date = models.DateField(blank=False)
sess_no = models.ForeignKey(Session,
on_delete=models.CASCADE)
parl_no = models.ForeignKey(Parliament,
on_delete=models.CASCADE)
class Meta:
unique_together = ('cir_no', 'sess_no',)
Using Circular model I will create different circulars. Suppose I will create Circular no-1 for the period of 31 August to 30 September 2016. In this case When I will view circular no-1 it will only shows those sittings that are between above mentioned dates inclusive. And this circular will also contain the above mentioned Ministry/Division table. Should I include the sitting template in the circular template?
How can I do that. Any help will be much appreciated.
Edit:
Here is sitting_list.html template:
{% block content %}
<table cellpadding="10">
<tr>
<th>Sitting Day & Date</th>
<th>Ministry/Division</th>
</tr>
{% for sitting in sittings %}
<tr>
<td> {{ sitting.sit_date|date:"l, d F, Y" }}</td>
<td>
{% for genre in sitting.genre.get_descendants %}
{{ genre }},
{% endfor %}
<p>(Ballot: {{ sitting.ballot_date}})</p>
</td>
</tr>
{% endfor %}
</table>
{% endblock content %}
I'm not sure if i completely understand you right. But one way to get sittings for the period of a circular (without an explicit model relation) could look as this:
class Circular(models.Model):
# ...
sit_start_date = models.DateField(blank=False)
sit_end_date = models.DateField(blank=False)
# ...
#property
def sittings(self):
qs = Sitting.objects.filter(sit_date__gte=self.sit_start_date, sit_date__lte=self.sit_end_date)
return qs
Then you can use this propperty on a 'circular' instance:
c = Circular.objects.get(pk=1)
c.sittings

Django check every objects of a query to see if have related objects and use this on template

I have a template with a table, and there I have all the objects on a query. Each object can have related objects or not. Assuming this, what I need to do is, for each object check if have or not that related object. If not in the table I have a field to put a link to create a related object, but if have show a icon to see this object.
I can do with the one of the "parent" object but I don't know how to do if I have more than one object in the query.
Models to check:
class Accident(models.Model):
employee = models.ForeignKey(Employee)
place = models.IntegerField(choices=ACCIDENT_PLACE, default=1)
detail = models.CharField(max_length=255)
clinic = models.ForeignKey(Clinic)
is_urgency = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
created_by = models.ForeignKey(User,
related_name='accidents_created_by',
editable=False,
blank=True,
null=True
)
modified_by = models.ForeignKey(User,
related_name='accidents_modified_by',
editable=False,
blank=True,
null=True
)
class Meta:
verbose_name = "accidente"
verbose_name_plural = "accidentes"
def __str__(self):
return str(self.id)
class AccidentCertificate(models.Model):
accident = models.ForeignKey(Accident)
diagnostic = models.CharField(max_length=150)
return_day = models.DateField()
notes = models.CharField(max_length=255)
medication = models.CharField(max_length=255)
doctor = models.ForeignKey(Doctor)
presented = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
created_by = models.ForeignKey(User,
related_name='acc_certificates_created_by',
editable=False,
blank=True,
null=True
)
modified_by = models.ForeignKey(User,
related_name='acc_certificates_modified_by',
editable=False,
blank=True,
null=True
)
class Meta:
verbose_name = "certificado de accidente"
verbose_name_plural = "certificados de accidente"
ordering = ["-id"]
def __str__(self):
return str(self.id)
this is my view (to check only one object that i already know that have 1 related object)
class EmployeeDetailView(LoginRequiredMixin, DetailView):
# Chequeamos que el usuario se encuentre logeado
login_url = reverse_lazy('users:login')
model = Employee
template_name = 'employees/detail.html'
pk_url_kwarg = 'employee_id'
def get_context_data(self, **kwargs):
context_object_name = 'employee'
context = super(EmployeeDetailView, self).get_context_data(**kwargs)
employee = context['employee']
context['cuil'] = employee.cuil[:2]+'-'+employee.cuil[2:10]+'-'+employee.cuil[-1:]
# Tomamos los accidentes correspondientes al empleado
# y los pasamos al contexto
employee_accidents = Accident.objects.filter(employee=employee)
context['accidents'] = employee_accidents
# Tomamos el certificado del accidente si existe
accident_certificate = AccidentCertificate.objects.get(accident=employee_accidents)
return context
and in the template
<table class="table table-striped">
<thead>
<tr>
<th>ID Acc.</th>
<th>Fecha</th>
<th>Cant. Días</th>
<th>Locación</th>
<th>Detalle</th>
<th>Clinica</th>
<th>Urgencia</th>
<th>Cargado por</th>
<th>Certificado</th>
<th>Segimiento</th>
</tr>
</thead>
<tbody>
{% for a in accidents %}
<tr>
<td>{{ a.id }}</td>
<td>{{ a.created|date }}</td>
<td>-</td>
<td>{{ a.get_place_display }}</td>
<td>{{ a.detail }}</td>
<td>{{ a.clinic }}</td>
<td>
{% if a.is_urgency %}
Si
{% else %}
No
{% endif %}
</td>
<td>{{ a.created_by }}</td>
<td>{% bootstrap_icon "search" %}</td>
<td>{% bootstrap_icon "search" %}</td>
</tr>
{% empty %}
<p class="text-center">
<strong>NO HAY REGISTROS</strong>
</p>
{% endfor %}
</tbody>
</table>
Well in sintesis I need to take every accidents that correspond to an employee, and for each accident check if this have an AccidentCertificate, if it have put the link in the table to see the certificate, and if not put the link to create the certificate.
You can use a count annotation to add the number of related certificates to each accident, then use that number in an if statement in the template.
from django.db.models import Count
...
employee_accidents = Accident.objects.filter(employee=employee).annotate(certificate_count=Count('accidentcertificate'))
...
{% for a in accidents %}
...
{% if a.certificate_count == 0 %}
Add new certificate
{% endif %}
{% endfor %}

Using ListView in django, getting __init__() takes exactly 1 argument (2 given)

I am very new to django, I am trying to display the list of albums i have in my database. This is the Album model
class Album(models.Model):
"""Album model"""
title = models.CharField(max_length=255)
prefix = models.CharField(max_length=20, blank=True)
subtitle = models.CharField(blank=True, max_length=255)
slug = models.SlugField()
band = models.ForeignKey(Band, blank=True)
label = models.ForeignKey(Label, blank=True)
asin = models.CharField(max_length=14, blank=True)
release_date = models.DateField(blank=True, null=True)
cover = models.FileField(upload_to='albums', blank=True)
review = models.TextField(blank=True)
genre = models.ManyToManyField(Genre, blank=True)
is_ep = models.BooleanField(default=False)
is_compilation = models.BooleanField(default=False)
class Meta:
db_table = 'music_albums'
ordering = ('title',)
def __unicode__(self):
return '%s' % self.full_title
My view is
class album_list(ListView):
template_name = "/music/album_list.html"
context_object_name = 'list_of_albums'
#paginate_by = '15'
def get_queryset(self):
return Album.objects.all()
I am able to add albums from the admin interface, but on going to the /albums/ url to display them, I get init() takes exactly 1 argument (2 given) error.
The template I am using
{% extends "music/base_music.html" %}
{% block title %}Music Albums{% endblock %}
{% block body_class %}{{ block.super }} music_albums{% endblock %}
{% block content_title %}
<h2>Music Albums</h2>
{% include "music/_nav.html" %}
{% endblock %}
{% block content %}
<table>
<tr>
<th>Band</th>
<th>Album</th>
</tr>
{% for album in list_of_albums %}
<tr class="{% cycle 'odd' 'even' %}">
<td class="band">{{ album.band }} </td>
<td class="album">{{ album.full_title }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
I have gone through the answers to similar questions already asked here, but couldn't get the code to work.
Usually this is because you forgot to put .as_view() in your urls.py:
Instead of
(r"", 'SomeName.views.album_list'),
put
(r"", SomeName.views.album_list.as_view()),
Remember to change SomeName :)