When displaying foreign key base Queryset on template - python

I have two models shown below
class Services(models.Model):
name = models.CharField(max_length=200, null=True)
price = models.FloatField(null=True)
service_sku = models.CharField(max_length=200, null=True)
class Order(models.Model):
customer = models.ForeignKey(Customer, null=True, on_delete = models.SET_NULL)
service = models.ForeignKey(Service, null=True, on_delete = models.SET_NULL)
I have created a function-based view based on the service model where I want to render a template displaying a list of field information from each instance from the service model.
See below for my views and template
views.py
def service_list(request):
service_list = Service.objects.all().order_by('service_sku')
context = {'service_list':service_list}
return render(request, 'accounts/service_list.html',context)
template
<div class="card card-body">
<table class="table">
<tr>
<th>SKU</th>
<th>Service Name</th>
<th>Number of Ordered</th>
</tr>
{% for i in service_list %}
<tr>
<td>{{i.service_sku}}</td>
<td>{{i.name}} </td>
<td></td>
</tr>
{% endfor %}
I want to display the number of orders of each instance, considering that the order model has a foreign key from the service model.
The query set would have to be like the below shown.
Order.objects.all().filter(service__id =3).count()
Problem is that I must specify which id of that model would be for that queryset to work.
I’ve tried to put the queryset format below within the template
{{Order.objects.all().filter(service__id=i.id).count()}
In the template it would look like how it does below
template
<div class="card card-body">
<table class="table">
<tr>
<th>SKU</th>
<th>Service Name</th>
<th>Number of Ordered</th>
</tr>
{% for i in service_list %}
<tr>
<td>{{i.service_sku}}</td>
<td>{{i.name}} </td>
<td>{{Order.objects.all().filter(service__id=i.id).count()}}</td>
</tr>
{% endfor %}
The above implementation does not work.
Which method is ideal for this situation?

You can not call methods (with parameters) in a Django template. The templates are deliberately restricted to prevent people from writing business logic in the templates.
You can .annotate(…) [Django-doc] the queryset to fetch the number of related Orders:
from django.db.models import Count
def service_list(request):
service_list = Service.objects.annotate(
norders=Count('order')
).order_by('service_sku')
context = {'service_list':service_list}
return render(request, 'accounts/service_list.html',context)
In the template you can then render this with:
{% for i in service_list %}
<tr>
<td>{{ i.service_sku }}</td>
<td>{{ i.name }} </td>
<td>{{ i.norders }}</td>
</tr>
{% endfor %}

Related

Name attribute of input field identical modelform

I am trying to loop through a Model PendingRequest and check the every request instance of a user and approve or decline the users request for each instance.I am using a ModelForm to render my form however, the part that gives issue is that the radio button to click on whether to approve or decline do not share the same input name attribue
here is my template
<form method="post">
{% csrf_token %}
<table>
<thead>
<tr>
<th>Book Title</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{% for book in books %}
<tr>
<td>{{ book.book.title }}</td>
<td>
<input type="radio" name="{{ book.id }}" value="approved"> Approve
<input type="radio" name="{{ book.id }}" value="not_approved"> Decline
</td>
</tr>
{% endfor %}
</tbody>
</table>
<button type="submit">Update</button>
</form>
so what i want to achieve is to replace
<input type="radio" name="{{ book.id }}" value="approved"> and <input type="radio" name="{{ book.id }}" value="not_approved"> with this below such that when it is rendered, every loop will have same the name attribute but different values
<form method="post">
{% csrf_token %}
<table>
<thead>
<tr>
<th>Book Title</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{% for book in books %}
<tr>
<td>{{ book.book.title }}</td>
<td>
{% for choice in form.approved %}
{{choice.tag}}
{% endfor %}
{% for choice in form.not_approved %}
{{choice.tag}}
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<button type="submit">Update</button>
</form>
here is my forms.py
class Approve(forms.ModelForm):
approved = forms.BooleanField(widget=forms.RadioSelect(choices=[(True, 'Approve')]))
not_approved = forms.BooleanField(widget=forms.RadioSelect(choices=[(True, 'Decline')]))
class Meta:
model = PendingRequest
exclude = ["member", "book_request", "book"]
models.py
class PendingRequest(models.Model):
book_request = models.OneToOneField(BorrowBook, on_delete=models.CASCADE, null=True)
member = models.ForeignKey(User, on_delete=models.CASCADE, default="", null=True)
book = models.ForeignKey(Books, on_delete=models.CASCADE, default="", null=True)
approved = models.BooleanField(default=False)
not_approved = models.BooleanField(default=False)
approval_date = models.DateTimeField(auto_now=True, null=True)
views.py
def approve(request, pk):
books = PendingRequest.objects.filter(member__id=pk)
if request.method == "POST":
for book in books:
form = Approve(request.POST, instance=book)
if form.is_valid():
book.approve = form.cleaned_data.get(f'approve_{book.id}')
book.save()
return redirect(index)
else:
form = Approve()
return render(request, "books/approve.html", {"form": form, "books": books})
You are making this a little complicated for yourself...
You have two booleans in that PendingRequest model. One for approved and one for not_approved. You only want one, because if approved is False, then it's already not_approved by definition.
You also are making life difficult by having two radio button fields with one radio button each for field. Radio buttons are for multiple choice, one value allowed. You could use two radio buttons for one field (a checkbox is often better) but having two radio buttons for two separate fields is not only bad HTML, you'll likely be able to select both at once. If you give them the same name to prevent that, they won't be linked to the correct field without a lot of weird post-POST fiddling about. It's very messy. I'd really advise using a single field.
Regardless if you take that advice, for multiple forms of the same model, Django can handle this situation with a modelformset, something like...
views.py
PendingRequestFormSet = modelformset_factory(PendingRequest , form = Approve)
#we have to also bring in the book title along with the forms , even though we are not using it in the form,
# so we'll zip up the queryset with the formset.
# We use select_related in the queryset to prevent additional database calls in the template when the title gets evaluated
pending_requests = PendingRequest.objects.filter(member__id=pk).select_related('book')
formset = PendingRequestFormSet(queryset=pending_requests))
pending_requests_and_formset = zip(queryset, formset)
...
return render(request, "books/approve.html", {"form": form, "books": books, 'pending_requests_and_formset', pending_requests_and_formset })
template.html
...
<form method="post">
{{ formset.management_form }}
{% for request, form in pending_requests_and_formset %}
<td>{{request.book.title}}</td>
<td>{{ form }}</td>
{% endfor %}
</form>

Access related table field value in django template

I am trying to access a Django related field value in a Django template:
{% for item in order %}
<tr>
<td>{{ item.id }}</td>
<td class="text-left">{{ item.created }}</td>
<td class="text-left">{{ item.payments__charge_status }}</td>
<td class="text-left">{{ item.shipping_method_name }}</td>
<td class="text-right">£{{ item.total_gross_amount }}</td>
</tr>
{% endfor %}
Actual queryset defined in views.py:
orders = Order.objects.values().filter(created__range=(start_date, end_date)).filter(Q(payments__charge_status="fully-charged") | Q(payments__charge_status="not-charged"))
Payment is in a different table and I can access using payments__charge_status in the view but not in the actual template.
Any help is appreciated.
Updated with models.py for Payment Model:
....
class Payment(models.Model):
order = models.ForeignKey(
Order, null=True, related_name="payments", on_delete=models.PROTECT
)
From your database modeling order can have multiple payments ( Many-to-One )so payments is a QuerySet you should iterate through
{% for payment in item.payments.all %}
{{ payment.charge_status }
{% endfor %}

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.

Django User Posts filter by year

In Django I needed user Posts filter by year, so I did the following in views:
Views:
from django.contrib.auth.models import User
from django.db.models import Count
from .models import Book
def about (request):
model = Book
context = {
'books': Book.objects.values('author')
.filter(date_posted__year=2019)
.annotate(total_books = Count('author'))
}
In the HTML
books.author.id
books.author
books.total_books
It worked, it shows the total but instead of printing de Author username, it prints the Author ID, and the author ID is blank.
Django Version 3.0.8
This is the complete HTML:
{% block content %}
<div class="container">
<div class="w-50">
<table class="table">
<thead class="thead-dark">
<tr>
<th scope="col">User ID</th>
<th scope="col">Username </th>
<th scope="col">Total Books</th>
</tr>
</thead>
{% for books in books %}
<tbody>
<tr>
<ul>
<td> {{ books.author }}</td>
<td> {{ books.author }} </td>
<td>{{ books.total_qcreports}}</td>
</ul>
</tr>
</tbody>
{% endfor %}
</table>
</div>
</div>
{% endblock content %}
Models:
class Book(models.Model):
title = models.CharField(max_length=100)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
This is how it shows in browser:
enter image description here
context = {
'books': Book.objects.values('author','author__username')
.filter(date_posted__year=2019)
.annotate(total_books = Count('author'))
}
HTML Template
{{book.author}}
{{book.author__username}}
{{book.total_book}}

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