How to check two models ID whitout nested loop? - python

My problem is the nested loop of my template... I use it to check if my model sitio.id_sitio has an equivalent ID with another model comprobante.id_sitio with a Foreign Key and then print A if one result is found and B if none
The conditional if work fine but I don't want the nested loop print multiple times.
If one result is found I want to break the cycle and print the HTML only one time, like Checkouts
Else if the result doesn't exist in the records at the end of the for I want to print <p>No payments</p>
I don't know if I have to write a query in the views.py or if I have to do something else in the templates...
Is there a correct way to do this?
This is my code:
Models.py
class Comprobante(models.Model):
id_sitio = models.ForeignKey('Sitio', models.DO_NOTHING, db_column='id_sitio', blank=True, null=True)
class Sitio(models.Model):
id_sitio = models.IntegerField(primary_key=True)
sitio = models.CharField(max_length=100, blank=True, null=True)
def __str__(self):
return self.sitio
Views.py
def topsitios(request):
sitio = Sitio.objects.all()[0:100]
comprobante = Comprobante.objects.all()[0:100]
context = {'sitio': sitio, 'comprobante': comprobante}
return render(request, "sitio_ptc/topsitios.html", context)
Template.html
{% block content %}
{% for s in sitio %}
<tr>
<th scope="row"> {{ forloop.counter }}</th>
<td> {{ s.sitio }} </td>
<td>
{% for c in comprobante %}
{% if s.id_sitio == c.id_sitio_id %}
Checkouts
{% else %}
<p>no payments</p>
{% endif %}
{% endfor %}
</td>
</tr>
{% endfor %}
{% endblock %}

The correct way of doing this is to use Django ORM to access a model's relationship. In the for-loop of each sitio, you can check if any related comprobante exists by {% if s.comprobante_set.all %}
For better performance, you would also need to prefetch related comprobante records by Sitio.objects.prefetch_related('comprobante_set').all(). This way you won't suffer from N+1 query problem.

Quick solution would be:
{% if c.id_sitio_id == s.id_sitio %}
Because Comprobante.id_sitio is a foreign key related to a model with a custom primary key, so c.id_sitio_id (_id is a suffix added by Django that keeps the value of primary key, that is usually id-field in most cases) stores the value of Sitio.id_sitio.
But I would refactor your code to make it clearer and more flexible like this:
# models.py
class Comprobante(models.Model):
sitio = models.ForeignKey('Sitio', on_delete=models.SET_NULL, blank=True, null=True)
class Sitio(models.Model):
name = models.CharField(max_length=100, blank=True, null=True)
# in the template
...
{% if s.id == c.sitio_id %}
...

You can use the related name attribute in Comprobanteclass
class Comprobante(models.Model):
id_sitio = models.ForeignKey(
'Sitio',
models.DO_NOTHING,
related_name='comprobantes',
db_column='id_sitio', blank=True, null=True)
once a related_name is set, you can check with it in your template
{% block content %}
{% for s in sitio %}
<tr>
<th scope="row"> {{ forloop.counter }}</th>
<td> {{ s.sitio }} </td>
<td>
{% if s.comprobantes %}
{# Hay un comprobante para el sitio #}
Checkouts
{% else %}
<p>no payments</p>
{% endif %}
</td>
</tr>
{% endfor %}
{% endblock %}
Hope this helps

Related

Django app not displaying data in template

im brand new to Django and attempting to make my first ECommerce site. I'm attempting to get a list of products from a .JSON file to display in my products.html template. The products are in place, as shown in the Django admin portal, productsis definied with in my views.py file and i've done loaddata but nothing comes through. I've added some screenshots and code below to further explain.
Views.py Code:
from django.shortcuts import render
from .models import Product
def all_products(request):
products = Product.objects.all()
context = {
'products': products,
}
return render(request, 'products/products.html', context)
products.html Template:
{% extends "base.html" %}
{% load static %}
{% block page_header %}
<div class="container header-container">
<div class="row">
<div class="col"></div>
</div>
</div>
{% endblock %}
{% block content %}
<div class="container">
<div class="row">
<div class="col">
{{ products }}
</div>
</div>
</div>
{% endblock %}
My folder structure is correct but I'll post it here just incase:
Any help would be great as i've been stuck with this for a while now. Thanks!
models.py:*
class Product(models.Model):
category = models.ForeignKey('category', null=True, blank=True, on_delete=models.SET_NULL)
sku = models.CharField(max_length=254, null=True, blank=True)
name = models.CharField(max_length=254)
description = models.TextField()
price = models.DecimalField(max_digits=6, decimal_places=2)
rating = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True)
image_url = models.URLField(null=True, blank=True)
image = models.ImageField(null=True, blank=True)
def __str__(self):
return self.name
You need to iterate through the object you are passing to your template.
For example,
{% for product in products %}
<img src={{ product.src }} />
<h3>{{ product.title }}</h3>
<p>{{ product.desc }}</p>
{% endfor %}
You have done most of the thing right so far. One thing that causes the issue is the template didn't iterate all the products.
In the views.py, Product.objects.all() returns a query set which you can imagine it is a list that contains many product instances.
So when the products, which is Product.objects.all(), is passed to template, you will need to use a loop to iterate the instances inside the products.
I assume your product model contains the following three fields(you can replace them with the actual field inside your product model later in the example I provide).
name
description
qty
Now, in Django template, you can replace the {{ products }} with the following code.
<table>
<thead>
<tr>
<th scope="col" class="text-nowrap"><span>Product</span></th>
<th scope="col" class="text-nowrap"><span>Description</span></th>
<th scope="col" class="text-nowrap"><span>Qty</span></th>
</tr>
</thead>
<tbody>
{% for product in products %}
<tr>
<td><span>{{ product.name }}</span></td><!-- Replace the name with your actual field -->
<td><span>{{ product.desciption }}</span></td><!-- Replace the desciption with your actual field -->
<td><span>{{ product.qty }}</span></td><!-- Replace the qty with your actual field -->
</tr>
{% endfor %}
</tbody>
</table>
With the above example, the most important part is
{% for product in products %}
<tr>
<td><span>{{ product.name }}</span></td>
<td><span>{{ product.desciption }}</span></td>
<td><span>{{ product.qty }}</span></td>
</tr>
{% endfor %}
It is similar to the following python code.
for product in products:
print(product.name)
print(product.desciption)
print(product.qty)
And as a result, now the template should show the product on a table.

Rendering models in template which are related themselves

I write a twitter-like app in django,
I've got two models:
class Tweet(models.Model):
content = models.CharField(max_length=140)
creation_date = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
class Replies(models.Model):
reply = models.CharField(max_length=140)
tweet = models.ForeignKey(Tweet, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.reply
And the fragment of template:
{% block content %}
{% for t in tweets %}
<table class="table1">
<br>
<tr>
<td>
#{{t.user}}
<br> <br>
{{t.content}}
<br> <br>
{{t.creation_date}}
</td>
#######
#######
</tr>
</table>
{% endfor %}
{% endblock %}
Between '####' i would like to have all the replies to the specific tweet, how can i do that?
Thanks in advance
something like this should do the trick:
{% block content %}
{% for t in tweets %}
<table class="table1">
<br>
<tr>
<td>
#{{t.user}}
<br> <br>
{{t.content}}
<br> <br>
{{t.creation_date}}
</td>
{% for reply in t.replies_set.all %}
{{reply.user}} {{reply.reply}}
{% endfor %}
</tr>
</table>
{% endfor %}
{% endblock %}
Here is a link to the documentation that explains many to one relationships in detail https://docs.djangoproject.com/en/3.0/topics/db/examples/many_to_one/
#Steffen - it works, however you need to remove '()' at the end of
for reply in t.replies_set.all()

Django modelformset_factory UnboundLocalError

I am getting error message the UnboundLocalError local variable 'Assumptions' referenced before assignment (after line else:). I wonder how is that even possible given that Assumptions is a model and is imported to the views.py. Any advise how to solve it would be highly appreciated. Also, I am trying to create multiple forms based on Assumptions modelform and save them into the database model Assumptions. Please advise if this code is the correct design pattern. Thank you.
views.py
from django.shortcuts import render
from .forms import modelformset_factory, AssumptionsForm
from .models import Assumptions
def get_assumptions(request):
if request.method == 'POST':
if 'name' in request.POST:
formset = modelformset_factory(Assumptions, form = AssumptionsForm, extra = 5)
if formset.is_valid():
print('valid form')
for form in formset:
print('Looping forms')
Assumptions = form.save(commit='False')
Assumptions.Name = 'name'
Assumptions.save()
else:
formset = modelformset_factory(Assumptions, form = AssumptionsForm, extra = 5)
return render(request, 'assumptions.html', {'formset': formset})
assumptions.html
<div class="form">
<form action="" method="post">
{% csrf_token %}
{{ formset.management_form }}
{{ formset.non_form_errors.as_ul }}
{% for name in ['A', 'B'] %}
<h1>var</h1>
<table id="formset" class="form">
{% for form in formset.forms %}
{% if forloop.first %}
<thead><tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr></thead>
{% endif %}
<tr class="{% cycle 'row1' 'row2' %}">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="submit", name='name', value="save" />
{% endfor %}
</form>
</div>
forms.py
from django import forms
from django.forms import modelformset_factory, ModelForm
from .models import Assumptions
class AssumptionsForm(ModelForm):
class Meta:
model = Assumptions
fields = ['Worst', 'Base', 'Best']
exclude = ['Name']
models.py
from django.db import models
from django.forms import ModelForm
class Assumptions(models.Model):
Worst = models.FloatField(null=True, blank=True, default=None)
Base = models.FloatField(null=True, blank=True, default=None)
Best = models.FloatField(null=True, blank=True, default=None)
Name = models.TextField(null=True, blank=True, default=None)
You are shadowing the imported Assumptions name by assigning a new value to it here:
Assumptions = form.save(commit='False')
You should use a different name for your model instance. The usual practice is to use a lowercase name, i.e.
assumptions = form.save(commit='False')
assumptions.Name = 'name'
assumptions.save()

Django: how to get list related ForeignKey records?

I am using Django v1.11. Here are 2 models:
class Task(models.Model):
title = models.CharField()
class TaskUser(models.Model):
task = models.ForeignKey(Task, related_name='task_user')
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='+')
So, I want to get list of tasks and print in one column list of users that are related to this task. How to do that? The problem is tried a lot: select_related, prefetch_related('task_user') and so on.. but nothing works. In some cases there are no sql error, but it prints task.User.None
tasks = Task.objects.all()
View must be:
<table>
<tr>
<td>Title</td>
<td>Users</td>
</tr>
{% for task in tasks %}
<tr>
<td>{{ task.title }}</td>
<td>
{% for user in task.users %}
{{ user.id }}
{% endfor %}
</td>
</tr>
{% endfor %}
</table>
Add a many-to-many field to Task, using the through model you already have:
class Task(models.Model):
users = models.ManyToManyField(settings.AUTH_USER_MODEL, through='TaskUser')
and use .all when iterating:
{% for user in task.users.all %}
{{ user.id }}
{% endfor %}
(Note, you should remove the related_name='+' from the TaskUser.user field.)

trying to get many-to-many related data in Django

I am building a website in Django where messages have tags. Below you can see my models.py
class Message(models.Model):
message_id = models.BigIntegerField(primary_key=True)
user = models.ForeignKey(User)
title = models.CharField(max_length=50)
tags = models.ManyToManyField(Tag, blank=True, null=True)
date_created = models.DateTimeField(auto_now_add=True)
def get_tags(self, tag_id):
return self.message.filter(tags__tag_id=tag_id)
class Tag(models.Model):
tag_id = models.BigIntegerField(primary_key=True)
tag_name = models.CharField(max_length=100)
date_created = models.DateTimeField(auto_now_add=True)
Here's my views.py as well.
popular_tags = Tag.objects.filter(messages__date_created__gt=now).annotate(num=Count('messages')).order_by('-num')[:25]
What I am trying to do here is that I want to show tags with the most number of messages. After I do that, I want the tags to show which messages have that tag. So I know I can't do query filtering in template, and that's why I added get_tags under the Message model. So with that I've tried writing the right template, but I am not sure what went wrong with it.
{% for q in popular_tags %}
<tr>
<td align="left"><h5>{{ q.tag_name }}</h5> </td>
{% for m in message.get_tags %}
<td align="left"><h5>{{ m.title }} </h5></td>
{% endfor %}
</tr>
{% endfor %}
I am not getting any errors, it's just that no messages are being shown while the tags are correctly displayed. Any ideas how and where I should fix?
You don't need anything special, Django's ORM takes care of it, cf https://docs.djangoproject.com/en/1.6/ref/models/relations/#related-objects-reference
The following should work:
{% for q in popular_tags %}
<tr>
<td align="left"><h5>{{ q.tag_name }}</h5> </td>
{% for m in q.message_set.all %}
<td align="left"><h5>{{ m.title }} </h5></td>
{% endfor %}
</tr>
{% endfor %}
You should add a function within Tag rather than Message:
class Tag(models.Model):
tag_id = models.BigIntegerField(primary_key=True)
tag_name = models.CharField(max_length=100)
date_created = models.DateTimeField(auto_now_add=True)
def get_msgs(self):
return Message.objects.filter(tags__tag_id=tag_id)
Hence, the template should be:
{% for q in popular_tags %}
<tr>
<td align="left"><h5>{{ q.tag_name }}</h5> </td>
{% for m in q.get_msgs %}
<td align="left"><h5>{{ m.title }} </h5></td>
{% endfor %}
</tr>
{% endfor %}

Categories