Django Master/Detail Template - django

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

Related

Accessing variable context name in template

I'm just discovering Django and i'm still a total noob.
I got a view that return multiple query set.
class RouteDateView(ListView):
model = Tblsummary
template_name = 'reviewrouteassess/selectdaterange.html'
def get_context_data(self, *args, **kwargs):
context = super(RouteDateView, self).get_context_data(*args, **kwargs)
context['allroute'] = Tblsummary.objects.filter(off_no=self.request.GET.get('depotcode')[:4])
for p in context['allroute']:
context[p.res_route] = Tblsummary.objects.filter(res_route=p.res_route)
return context
Then in my template I would like to access those dynamically name context. That's where i'm stuck.
<table>
{% for a in allroute %}
<tr>
<td>{{ a.res_route }}</td>
<td>{{ a.hcalls }}</td>
</tr>
{% for d in a.res_route %}
<tr>
<td>{{ d.res_route }}</td>
<td>{{ d.hcalls }}</td>
</tr>
{% endfor %}
{% endfor %}
How can I evaluate a.res_route so that a.res_route return the context passed by the view?? Thanks you so much!!
{% for d in a.res_route %}
I would suggest adding a method to your model to access the second query:
class Tblsummary(models.Model):
... # or whatever is there currently
def sub_routes(self): # name this how you'd like
return Tblsummary.objects.filter(res_route=self.res_route)
And then in your template:
<table>
{% for a in allroute %}
<tr>
<td>{{ a.res_route }}</td>
<td>{{ a.hcalls }}</td>
</tr>
{% for d in a.sub_routes %}
<tr>
<td>{{ d.res_route }}</td>
<td>{{ d.hcalls }}</td>
</tr>
{% endfor %}
{% endfor %}
This isn't going to be ideal from an efficiency standpoint since the sub_routes query is called once per entry in allroute but right now allroute is limited to 4 results so that shouldn't be an issue in practice.

How to filter data displayed in a table in django?

I have a project where when a user enters a vehicle no, the database is filtered and a table containing that vehicle no and the information corresponding to is displayed. I further want to filter this displayed table, eg: if the user chooses to see quantity greater than 18kl, then the matching vehicle number with quantity greater than 18 is displayed. Also I want to hide the columns selected by the users as there are many columns. Can someone tell me how to do this in django, or suggest some better ways. (I am providing only the related code snippet.)
forms.py
class VehicleSearch(forms.Form):
vehicl[![enter image description here][1]][1]e_no = forms.CharField(widget=forms.TextInput(attrs={'class': 'special'}), required=False)
#filter form
class Filter(forms.Form):
capacity_grp = forms.ChoiceField(label='Show only', widget=forms.RadioSelect,
choices=[('abv', '>18 kl'), ('blw', '<18 kl')], required=False)
views.py
def search(request):
form_1 = forms.VehicleSearch()
if request.method == 'POST' and 'btnform1' in request.POST:
form_1 = forms.VehicleSearch(request.POST)
if form_1.is_valid():
vehicle_no = form_1.cleaned_data['vehicle_no']
transport = models.Transport.objects.filter(vehicle=vehicle_no)
my_dict.update({'transport': transport})
return render(request, 'search.html', my_dict)
search.html
/Vehicle form/
<form id="f1" method="POST">
{% csrf_token %}
{{form_1.as_p}}
<p style="padding: 10px;"><button class="myButton" name="btnform1">Search</button></p>
</form>
/*Table display*/
<div class="submain">
{% if transport %}
<table id="transportation">
<thead>
<th>Vehicle</th>
<th>Carrier</th>
<th>Location No</th>
<th>MCMU</th>
<th>Location</th>
<th>Customer Code</th>
<th>Zone</th>
<th>Quantity</th>
<th>RTKM</th>
<th>KL* KM</th>
<th>Amount</th>
<th>Load</th>
<th>Capacity</th>
<th>Rate</th>
<th>Cost</th>
</thead>
{% for i in transport %}
<tr class="item">
<td>{{ i.vehicle }}</td>
<td>{{ i.carrier }}</td>
<td>{{ i.location_no }}</td>
<td>{{ i.mcmu }}</td>
<td>{{ i.location }}</td>
<td>{{ i.customer_code }}</td>
<td>{{ i.zone }}</td>
<td>{{ i.quantity }}</td>
<td>{{ i.rtkm }}</td>
<td>{{ i.klkm }}</td>
<td>{{ i.amount }}</td>
<td>{{ i.load }}</td>
<td>{{ i.capacity }}</td>
<td>{{ i.rate }}</td>
<td>{{ i.cost }}</td>
</tr>
{% endfor %}
</table>
</div>
In the table display you can add a loop with the name's field, something like that:
View:
def search(request):
form_1 = forms.VehicleSearch()
if request.method == 'POST' and 'btnform1' in request.POST:
form_1 = forms.VehicleSearch(request.POST)
columns = request.POST.getlist('filter_hide_columns')
if form_1.is_valid():
vehicle_no = form_1.cleaned_data['vehicle_no']
transport = models.Transport.objects.filter(vehicle=vehicle_no)
my_dict.update({'transport': transport}, {'columns': columns})
return render(request, 'search.html', my_dict)
TemplateTag
def lookup(model, attr):
if hasattr(model, attr):
return getattr(model, attr)
else:
return None
refer https://docs.djangoproject.com/en/2.2/howto/custom-template-tags/
Template:
{% if transport %}
<table id="transportation">
<thead>
<tr>
{% for field in columns %}
<th>{{ field.name }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for t in transport %}
<tr>
{% for field in columns %}
<td>{{ t|lookup:field.name }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
Please refer https://docs.djangoproject.com/en/2.2/topics/forms/#looping-over-the-form-s-fields

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 ForeignKey in ManytoMany field is displayed as None

I got a model that uses a foreignkey in a manytomany field:
class DefinitionTag(models.Model):
tag = models.CharField(max_length=50, default="Tag")
def __str__(self):
return self.tag
class Definition(models.Model):
name = models.CharField(max_length=100)
definition = models.CharField(max_length=1000)
fundstellen = models.CharField(max_length=300, blank=True)
wissenswertes = models.CharField(max_length=1000, blank=True)
tags = models.ManyToManyField(DefinitionTag)
def __str__(self):
return self.name
this works, and in the admin everything is set up so i can use it. The problem is if i try to display a table with the database entries in my view with the code:
def home(request):
query_results = Definition.objects.all()
context = {
"query_results": query_results
}
return render(request, 'home.html', context)
and in html:
{% for item in query_results %}
<tr>
<td>{{ item.name }}</td>
<td>{{ item.definition }}</td>
<td>{{ item.fundstellen }}</td>
<td>{{ item.wissenswertes }}</td>
<td>{{ item.tags }}</td>
</tr>
{% endfor %}
In the tag column it gives me only:
DefinitionTag.None
How can i display there all the tags choosen in the manytomany field?
I hope i'll get a hint!
Thanks
You need to loop on the m2m field to get all tags. Just referring to it won't give you individual tags that link to it:
{% for item in query_results %}
<tr>
<td>{{ item.name }}</td>
<td>{{ item.definition }}</td>
<td>{{ item.fundstellen }}</td>
<td>{{ item.wissenswertes }}</td>
<td>
{% for tag in item.tags.all %}
{{ tag }}
{% endfor %}
</td>
</tr>
{% endfor %}
If you are not sure how to get all items from m2m field, check out django doc.
Try looping through the tag:
{% for tag in item.tags.all %}
{{ tag }}
{% endfor %}

Grouping models in template

I have two related models:
class Package(models.Model):
package_name = models.CharField(max_length=64)
ptype = models.CharField(max_length=16)
class Extra(models.Model):
package = models.ForeignKey(Package, related_name='package')
data = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
information = models.CharField(max_length=1024, blank=True)
view.py:
def show_package(request):
list = get_list_or_404(Package, ptype='sometype')
return render(request, 'table.html', {'list': list})
and template:
{% for row in list %}
<table class="provider_list">
<tr>
<td>{{ row.package_name}}</td>
<td>{{ row.ptype }}</td>
</tr>
</table>
{% endfor %}
How can I add an additional table (from Extra model) next to the prelated table?
Like:
<table>
{% for extra_row in extra_list %}
<tr>
<td>{{ extra_row.data }}</td>
<td>{{ extra_row.information }}</td>
</tr>
{% endfor %}
</table>
Thanks.
try this:
{% for package in list %}
<table class="provider_list">
<tr>
<td>{{ package.package_name}}</td>
<td>{{ package.ptype }}</td>
<td>
<!-- fetch the related data -->
<table>
{% for extra in package.extra_set.all %}
<tr>
<td>{{ extra.data }}</td>
<td>{{ extra.information }}</td>
</tr>
{% endfor %}
</table>
</td>
</tr>
</table>
{% endfor %}
You show modify your Extra related_name on package ForeignKey.
models.py
class Extra(models.Model):
extra = models.ForeignKey(Package,related_name='extra_related_name') #default for related_name would be 'extra_set'
You can access all extra fields via:
extras = list.extra_related_name.all()
Let's imagine you have only one extra existing on every Package model
views.py
def show_package(request):
list = get_list_or_404(Package, ptype='sometype')
list.extra = list.extra_related_name.all()[0]
return render(request, 'table.html', {'list': list})
template
{% for row in list %}
<table class="provider_list">
<tr>
<td>{{ row.package_name}}</td>
<td>{{ row.ptype }}</td>
<td>{{ row.extra.data }}</td>
<td>{{ row.extra.information }}</td>
</tr>
</table>
{% endfor %}
If you are sure that there is at most one extra per package you should look at OneToOneField for easier access. If you are unsure, stick to ForeignKey and add checks in the view to check that you are accessing valid data.