This {{ category.id }} returns me 1
This {{ categories_counts.1 }} returns me data
But {{ categories_counts.category.id }} doesn't work?
Is it possible to access value by other object value?
views.py:
categories = Category.objects.all()
categories_counts = {}
for category in categories:
count = Venue.objects.filter(category_id=category.id).count()
categories_counts[category.id] = count
So categories containst:
<QuerySet [<Category: restaurants>, <Category: sportfitness>, <Category: carservices>, <Category: beautysalons>]>
categories_counts contains:
{1: 1, 2: 0, 3: 0}
category Model:
class Category(models.Model):
name = models.CharField(max_length=20)
bg_name = models.CharField(max_length=20, default=None)
category_bg_name = models.CharField(max_length=100, default=None)
In general, you can't access categories_counts[category.id] in the Django template unless you create a custom template tag, as #sayse says in the comments.
In your specific case, you can annotate the queryset with the venue counts.
from django.db.models import Count
categories = Category.objects.annotate(num_venues=Count('venue'))
Then in your template you can do something like:
{% for category in categories %}
{{ category.name }} has {{ category.num_venues }} venue(s)
{% endfor %}
Related
I have a Product Model and I want to be able to count all it's objects in a method so I can render the total number in a template, same for each category but I can only do that with the number_of_likes method.
class Category(models.Model):
name = models.CharField(max_length=45)
...
class Product(models.Model):
author = models.ForeignKey(User, default=None, on_delete=models.CASCADE)
title = models.CharField(max_length=120, unique=True)
category = models.ForeignKey(Category, default=None, on_delete=models.PROTECT)
product_type = models.CharField(max_length=30, choices=TYPE, default='Physical')
likes = models.ManyToManyField(User, related_name='like')
...
def number_of_likes(self):
return self.likes.count()
def number_of_products(self):
return self.Products.objects.all.count()
def number_of_products_for_category(self):
return Product.objects.filter(category_id=self.category_id).count()
def __str__(self):
return str(self.title) + ' from ' + str(self.author)
<div>
<h3>All products ({{ number_of_products }})</h3>
{% for category in categories %}
<p>{{ category.name }} (q)</p>
{{ number_of_products_for_category }}
{% endfor %}
</div>
The number_of_products and number_of_products_for_category are the methods that aren't working.
You can work with:
def number_of_products_for_category(self):
return Product.objects.filter(category_id=self.category_id).count()
But if you use this for all categories, you can .annotate(..) [Django-doc] the queryset:
from django.db.models import Count
categories = Category.objects.annotate(num_products=Count('product'))
and render with:
{% for category in categories %}
<p>{{ category.name }} ({{ category.num_products }})</p>
{% endfor %}
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Here is my Model.py
class BlogModel(models.Model):
blog_id = models.AutoField(primary_key=True)
title = models.CharField(max_length=1000)
content = FroalaField()
user = models.ForeignKey(User, blank=True , null=True , on_delete=models.CASCADE)
image = models.ImageField(upload_to='public')
created_at = models.DateTimeField(auto_now_add=True)
upload_to = models.DateTimeField(auto_now=True)
Here is my View.py
def my_blogs(request):
d = BlogModel.objects.all().filter(user = request.user)
return render(request,"my_blogs.html",{'message' : d})
But when I try to get the blog_id and created_at fields then it will shows an error that the requested fields are not present in the respective table.
But you can see that the field is n the table itself.
Please help
Here is the error i am getting
enter image description here
Django Queryset d is a list of objects. You have to loop through it:
for each_blog in d:
each_blog.blog_id
This is how you access the data for a filter method query set.
BlogModel.objects.all().filter(user = request.user) returns a query set i.e. a list of blogs satisfying the conditions.
You might want to iterate through all the blogs in order to display it, which can be done as follows:
{% for blog in d %}
{{ blog.blog_id }}
{% endfor %}
Try in views.py:
from .models import BlogModel
def my_blogs(request):
blogs = BlogModel.objects.all()
return render(request, 'my_blogs.html', locals())
In your html template my_blog.html try this:
{% for b in blogs %}
{{ b.id }}: {{ b.created_at }}
{% endfor %}
You can write in views.py:
def my_blogs(request):
# filtered queryset for login user
queryset = BlogModel.objects.filter(user=request.user)
return render(request,"my_blogs.html",{'blogs': queryset})
and in template my_blogs.html:
{% for blog in blogs %}
{{ blog.id }}
{{ blog.created_at }}
{% endfor %}
The BlogModel.objects.filter(user=request.user) returns a queryset which is list of your model objects. You can access object by iterating the queryset in template.
This is my html file
{% for member in member_list %}
{% for batting in batting_list %}
{% if member.id == batting.member.id %}
{{ batting }}
<br>
{{ batting.match.id }}
<br>
{{ batting.runs }}
<br>
<hr>
{% endif %}
{% endfor %}
{% endfor %}
This is my models.py file
class Member(models.Model):
name = models.CharField(max_length=40, default='')
def __str__(self):
return str(self.name)
class Batting(models.Model):
member = models.ForeignKey(Member, on_delete=models.CASCADE, default='')
runs = models.IntegerField(blank=True, null=True)
match = models.ForeignKey(Match, on_delete=models.CASCADE, default='')
def __str__(self):
return str('{0} {1} scored {2} runs'.format(self.member, self.match.date, self.runs))
I am trying to figure out how I show the max runs for a member x in the html file. Currently I have been able to do it for the whole Batting table but not for the individual member! Any help please
You can query with:
from django.db.models import Max
member.batting_set.aggregate(
max_runs=Max('batting__runs')
)['max_runs']
This will be None if no related Battings exist.
You can annotate the Members queryset with:
from django.db.models import Max
member_list = Member.objects.annotate(
max_runs=Max('batting__runs')
)
and thus then render this with:
{% for member in member_list %}
{{ member.max_runs }}
{% endfor %}
You can use annotate over members to get their batting with highest runs value.
from django.db.models import Max
Member.objects.annotate(max_runs=Max("batting_set__runs"))
Then show {{ member.max_runs }} on your template.
You need to create annotation in your queryset which will contain highest runs for a member.
from django.db.models import Max
Member.objects.annotate(max_runs=Max('batting__runs'))
You can access it like a regular field
member_object.max_runs
I'm not sure about your view, but for a ListView the class will look like this
class MemberView(ListView):
template_name = 'tmp.html'
model = Member
def get_queryset(self):
return Member.objects.annotate(max_runs=Max('batting__runs')).all()
I have a model that looks like this
class Invoice(models.Model):
inv_number = models.CharField(max_length=10, primary_key=True)
customer = models.ForeignKey(Customer, on_delete=models.PROTECT)
inv_date = models.DateField()
class Txn(models.Model):
invoice = models.ForeignKey(Invoice, on_delete=models.PROTECT)
transaction_date = models.DateField()
reference = models.CharField(max_length=12)
amt = models.IntegerField()
I would like to display a report in my template that lists filtered invoices each with a sub-list of filtered transactions.
In my view I have done the following:
invoice_list = Invoice.objects.filter(customer=customer)
Which I pass into my template. In the template, I do something like the following:
{% for invoice in invoice_list %}
{{ invoice.inv_number }}, {{ invoice.customer}}, {{ invoice.inv_date }}
{% for txn in invoice.txn_set.all %}
{{ txn.transaction_date }}, {{ txn.reference }}, {{ txn.amt }}
{% endfor %}
{% endfor %}
This works great to show the entire transaction list per filtered invoice. The question is, how does one also filter the list of transactions per invoice in the template - what if I wanted just the transactions within a certain date range or that match a specific reference? Is there maybe a way to pass a filter to the txn_set queryset per invoice in the view before putting the main queryset in the context and without converting them to lists?
Thank you for any response!
Suggestion: collect the invoices and transactions in the view, not in the template.
With this view code you can reduce the amount of queries to 1, so it is a lot more optimal than your code (with queries the Txn table for each invoice):
# build basic query
qs = Txn.objects.select_related('invoice')\
.filter(invoice__customer=customer)\
.order_by('transaction_date')
# add filtering; here is an example, but it could be with date filter as well
reference = request.GET.get('reference', '')
if len(reference) > 0:
qs = qs.filter(reference__icontains=reference)
invoice_dict = {}
for txn in qs:
# add the Invoice if it does not exist yet
if txn.invoice_id not in invoice_dict:
invoice_dict[txn.invoice_id] = {
'invoice': txn.invoice,
'txn_list': [],
}
# add the Txn
invoice_dict[txn.invoice_id]['txn_list'].append(txn)
# sort by Invoice date
invoice_data_list = sorted(
invoice_dict.values(),
key=lambda x: x['invoice'].inv_date)
Then in your template:
{% for elem in invoice_data_list %}
{{ elem.invoice.inv_number }}
{{ elem.invoice.customer}}
{{ elem.invoice.inv_date }}
{% for txn in elem.txn_list %}
{{ txn.transaction_date }}
{{ txn.reference }}
{{ txn.amt }}
{% endfor %}
{% endfor %}
I got these models in my models.py
class Rating(models.Model):
stars = models.IntegerField()
user_rating = models.ForeignKey(UserProfile, related_name="rater", on_delete=models.CASCADE)
user_rated = models.ForeignKey(UserProfile, related_name="rated", on_delete=models.CASCADE)
ride_number = models.ForeignKey(Ride, related_name="rider", on_delete=models.CASCADE)
class UserProfile(models.Model):
user = models.ForeignKey(User)
And my views
def user_view(request):
avg = user_avg
total = user_count
context = {
'avg': avg,
'total': total
}
return render(request, 'rating/home.html', context)
def user_avg():
avg = (Rating.objects
.values('user_rated').annotate(Avg('stars'))
.distinct())
return avg
and lastly my template looks like this
{% for rating in avg %}
{{ avg }}
{% endfor %}
Right now it renders like this;
[{'user_rated': 1L, 'stars__avg': 3.2222}, {'user_rated': 2L, 'stars__avg': 3.625}] [{'user_rated': 1L, 'stars__avg': 3.2222}, {'user_rated': 2L, 'stars__avg': 3.625}]
I've tried changing the template to {{ avg.user_rated }} but then it renders me nothing, so I'm not sure what im doing wrong here. And how do I get the name of the users avg rating instead of user_rated
This line in your template:
{{ avg }}
should be changed to:
{{ rating.user_rated }}: {{ rating.stars__avg }}
full template:
{% for rating in avg %}
{{ rating.user_rated }}: {{ rating.stars__avg }}
{% endfor %}
Your 2 errors: 1 - loop var is rating - you should use it to access data in loop, 2 - to access data in dict you should use dot notation, check docs.
Docs: https://docs.djangoproject.com/en/2.0/topics/templates/#variables
Dictionary lookup, attribute lookup and list-index lookups are
implemented with a dot notation:
{{ my_dict.key }}
{{ my_object.attribute }}
{{ my_list.0 }}
To get user name in results of annotation you should add it to values in query:
# assuming that your UserProfile model have name field
avg = (Rating.objects.values(
'user_rated', 'user_rated__name').annotate(Avg('stars')).distinct())
But be careful with values and annotations - order is matter and resulting values is depend on values list:
Docs: https://docs.djangoproject.com/en/2.0/topics/db/aggregation/#order-of-annotate-and-values-clauses
As with the filter() clause, the order in which annotate() and
values() clauses are applied to a query is significant. If the
values() clause precedes the annotate(), the annotation will be
computed using the grouping described by the values() clause.
avg is an array of Rating objects, which is exactly what you're seeing in the render. Try {{ rating.user_rated }}.
In answer to your second question, you need to join the Rating table with the UserProfile and User tables, assuming that the User table contains the users' names. See this link for how to do that.