How to get the top 10 users, whose question with most answers? I did it by using 2 requests to db, but I need in one request, if possible.
P.S. I'm using MySQL, there is no 'distinct'
Result format:
User.username | Question.id | number of comments
I have the models:
from django.contrib.auth.models import User
...
class Category(models.Model):
name = models.CharField(max_length=255)
class Question(models.Model):
header = models.CharField(max_length=255)
author = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
creation_date = models.DateTimeField(default=datetime.datetime.now)
view_count = models.IntegerField(default=0)
content = models.TextField()
category = models.ForeignKey(Category, null=True, on_delete=models.SET_NULL)
class Answer(models.Model):
question = models.ForeignKey('Question')
author = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
content = models.TextField()
My realization with 2 requests:
def select5():
print 'SELECT 5'
N = 10
top_n_questions = Answer.objects.all().values('question').annotate(ans_count=Count('question')).order_by('ans_count').reverse()[:N]
top_n_questions_id = []
top_n_questions_rate = []
top_n_users = []
print top_n_questions
for dic in top_n_questions:
top_n_questions_id.append(dic['question'])
top_n_questions_rate.append(dic['ans_count'])
top_n_questions = Question.objects.filter(pk__in=top_n_questions_id)
for question in top_n_questions:
top_n_users.append(question.author)
for i in range(N):
print '%s question id: %d, rate: %d' % (top_n_users[i].username,\
top_n_questions_id[i], top_n_questions_rate[i])
UPD: I found how to do it:
def select6():
print 'SELECT 6'
print 'bla-bla-bla'
top_questions = Question.objects.annotate(answers=Count('answer')).order_by('-answers').select_related('author')[:10]
for question in top_questions:
print '%s question id: %d, rate: %d' % (question.author, question.id, question.answers)
1) By logic you need something like that below (but I think it will work only if question with user has one_to_one relation), so I think it doesn't work, but you can try it =) :
top_users = User.objects.annotate(ans_count=Count('question_set__answer_set')).order_by('-ans_count')[:10]
2) Solution that I suggest (I think is not best, but simple one) is additional field answer_count in Question that store count of answer so I can sort or filter by that value. This field updates after every Answer.objects.create() by post save signal:
#receiver(post_save, sender=Answer)
def update_answer_count(sender, instance, created, **kwargs):
question = instance.question
question.answer_count = sender.objects.filter(question=question).count()
question.save(update_fields=['answer_count'])
so after this you can do:
top_users = User.objects.order_by('-question_set__answer_count')[:10]
and in your template:
{% for user in top_users %}
{% for question in user.question_set.all %}
{{ user.username }}
{{ question.id }}
{{ question.answer_count }}
{% endfor %}
{% endfor %}
This is Laravel's ORM
User::orderBy('answers', 'desc')->take(10)->get();
something like this mate but for DJango!
Entry.objects.all().order_by('answers')[:5]
Related
I have two models interrelated items and broken :
class Items(models.Model):
id = models.AutoField(primary_key=True)
item_name = models.CharField(max_length=50, blank=False)
item_price = models.IntegerField(blank=True)
item_quantity_received = models.IntegerField(blank=False)
item_quantity_available = models.IntegerField(blank=True)
item_purchased_date = models.DateField(auto_now_add=True, blank=False)
item_units = models.CharField(max_length=50, blank=False)
def __str__(self):
return self.item_name
class Broken(models.Model):
item = models.ForeignKey(Items, default=1, on_delete=models.CASCADE)
item_quantity_broken = models.IntegerField(blank=True)
item_broken_date = models.DateField(auto_now_add=True, blank=False)
item_is_broken = models.BooleanField(default=True)
date_repaired = models.DateField(auto_now=True, blank=True)
def __str__(self):
return self.item.item_name
I wrote this view function to retrieve data to a table into a template:
def broken_items(request):
br = Broken.objects.select_related('item').all()
print(br.values_list())
context = {
'title': 'broken',
'items': br,
}
return render(request, 'store/broken.html', context)
this is the executing query:
SELECT "store_broken"."id",
"store_broken"."item_id",
"store_broken"."item_quantity_broken",
"store_broken"."item_broken_date",
"store_broken"."item_is_broken",
"store_broken"."date_repaired",
"store_items"."id",
"store_items"."item_name",
"store_items"."item_price",
"store_items"."item_quantity_received",
"store_items"."item_quantity_available",
"store_items"."item_purchased_date",
"store_items"."item_units"
FROM "store_broken"
INNER JOIN "store_items"
ON ("store_broken"."item_id" = "store_items"."id")
looks like it gives me all the fields I want. In debugger it shows data from both tables,
so I wrote for loop in template,
{% for item in items %}
<tr>
<td>{{item.id}}</td>
<td>{{item.item_id}}</td>
<td>{{item.item_quantity_broken}}</td>
<td>{{item.item_broken_date}}</td>
<td>{{item.item_is_broken}}</td>
<td>{{item.date_repaired}}</td>
<td>{{item.item_name }}</td>
<td>{{item.item_item_quantity_received}}</td>
<td>{{item.item_quantity_available}}</td>
<td>{{item.item_purchased_date}}</td>
<td>{{item.items_item_units}}</td>
</tr>
{% endfor %}
The thing is this loop only gives me data from broken table only. I can't see data from Items table.
can someone help me to find the reason why other details are not showing?
Your items query is of Broken objects. So in order to access the Items values you need to change your table. For better understanding change your view like this:
brokens = Broken.objects.select_related('item').all()
context = {
'title': 'broken',
'brokens ': brokens,
}
and then your table:
{% for broken in brokens %}
<tr>
<td>{{broken.id}}</td>
<td>{{broken.item.pk}}</td> # This is the item id
<td>{{broken.item_quantity_broken}}</td>
<td>{{broken.item_broken_date}}</td>
<td>{{broken.item_is_broken}}</td>
<td>{{broken.date_repaired}}</td>
<td>{{broken.item.item_name}}</td>
<td>{{broken.item.item_quantity_received }}</td>
<td>{{broken.item.item_quantity_available}}</td>
<td>{{broken.item.item_purchased_date}}</td>
<td>{{broken.item.items_item_units}}</td>
</tr>
{% endfor %}
you loop over a List of Broken objects
to access the related item objects
item.item.item_name
At the moment I'm working on a Django webapplication which is supposed to search through a database with historical persons ('Person'). These persons had offices ('Amt') in different locations ('Ort'). Here are the models:
from django.db import models
class Person(models.Model):
Sortiername = models.CharField(max_length=100)
Ansichtsname = models.CharField(max_length=200)
Erste_Erwaehnung = models.CharField(max_length=100)
GND = models.CharField(max_length=100)
Wikidata = models.CharField(max_length=100)
Bemerkung = models.CharField(max_length=2500)
class Amt(models.Model):
Person = models.ForeignKey(Person, on_delete=models.CASCADE)
AmtDeutsch = models.CharField(max_length=50)
AmtLatein = models.CharField(max_length=50)
Klerus = models.CharField(max_length=10)
Ort = models.ForeignKey(Ort, on_delete=models.CASCADE)
AmtStart = models.DateField()
AmtEnde = models.DateField()
class Ort(models.Model):
OrtDeutsch = models.CharField(max_length=50)
OrtLatein = models.CharField(max_length=50)
GND = models.CharField(max_length=100)
GPS = models.CharField(max_length=100)
I built this filter using django-filters, which allows me to filter the persons by all the necessary parameters:
import django_filters
from .models import Person, Amt, Ort
class PersonFilter(django_filters.FilterSet):
Ansichtsname = django_filters.CharFilter(lookup_expr='icontains', label='Name')
#the following two paragraphs are filtering all categories of offices ('Amt') to feed a dropdown menu in the search template
get_aemter_dt = Amt.objects.all()
aemter_dt_lst_1 = []
for amt in get_aemter_dt:
if amt.AmtDeutsch != "nicht vorhanden":
aemter_dt_lst_1.append((amt.AmtDeutsch, amt.AmtDeutsch))
aemter_dt_lst_2 = sorted(list(set(aemter_dt_lst_1)))
aemter_dt_tpl = tuple(aemter_dt_lst_2)
amt__AmtDeutsch = django_filters.ChoiceFilter(lookup_expr='icontains', label="Amt (Deutsch)", choices = aemter_dt_tpl)
get_aemter_lat = Amt.objects.all()
aemter_lat_lst_1 = []
for amt in get_aemter_lat:
if amt.AmtLatein != "nicht vorhanden":
aemter_lat_lst_1.append((amt.AmtLatein, amt.AmtLatein))
aemter_lat_lst_2 = sorted(list(set(aemter_lat_lst_1)))
aemter_lat_tpl = tuple(aemter_lat_lst_2)
amt__AmtLatein = django_filters.ChoiceFilter(lookup_expr='icontains', label='Amt (Latein)', choices = aemter_lat_tpl)
#Basic django-filters stuff
Erste_Erwaehnung = django_filters.CharFilter(lookup_expr='icontains', label='Erste Erwähnung')
GND = django_filters.CharFilter(lookup_expr='icontains', label='GND')
Wikidata = django_filters.CharFilter(lookup_expr='icontains', label='Wikidata')
class Meta:
model = Person
fields = ['Ansichtsname','amt__AmtDeutsch', 'amt__AmtLatein',]
And this is the template:
{% block content %}
<form method="get">
{{ person_filter.form.as_table }}
<button type="submit">Suche</button>
</form>
<ul>
{% for person in person_filter.qs %}
<li>{{ person.Ansichtsname }}</li>
{% endfor %}
</ul>
{% endblock %}
I know, it's probably a bit messy, but it does what I want it to do. Except for one thing: As you can see, each office has a start date ("AmtStart") and an end date ("AmtEnde"). I now need to integrate a search filter/form, that allows me to query by a date range. However, the date range is supposed to take both the start AND end date into account (I know how to do it with a single one). I will give an example: Let's say there's five persons in the DB with an office each:
A (AmtStart = 0875-01-01; AmtEnde = 0880-01-01)
B (AmtStart = 0840-01-01; AmtEnde = 0890-01-01)
C (AmtStart = 0810-01-01; AmtEnde = 0876-01-01)
D (AmtStart = 0877-01-01; AmtEnde = 0895-01-01)
E (AmtStart = 0810-01-01; AmtEnde = 0820-01-01)
When I search for the date range 0874-01-01 to 0881-01-01 I want to match every person of the above, except for person E.
I have tried several things, but they didn't work at all. The solution in my opinion would be, to build a query that grabs each person, where either AmtStart <= search start date and AmtEnde >= search end date, or AmtStart/AmtEnde is in between the two dates. How can I combine this into a single query, and how can I integrate it in my filter/template?
Thank you, if you need any more info, let me know!
Edit: Added imports and deleted unnecessary stuff, you should be able to run the code now.
trying to display the final mark per student. But instead of just showing the result of sum , it displays a chunk of query set: for example:
Annie <QuerySet [{'studName': None, 'attendance__sum': 3}, {'studName': 1, 'attendance__sum': 2}, {'studName': 2, 'attendance__sum': 1}]>
and same goes to the rest of the students.
I would like to display it like :
Annie 2
Benny 3
Charlie 4
My view:
def attStudName(request):
studentName = MarkAtt.objects.all()
mark = MarkAtt.objects.values('studName').annotate(Sum('attendance'))
context = {
'studentName' : studentName,
'mark' : mark
}
return render(request,'show-name.html',context)
My template:
{% for y in mark %}
{% for x in studentName %}
<p>{{x.studName}}</p> <p> {{mark}}</p>
{% endfor %}
{% endfor %}
How do i display each student name with their own mark accordingly? And how do i display the mark without the
Edited:
Model.py:
class Namelist(models.Model):
name = models.CharField(max_length=100)
program = models.CharField(max_length=10)
year = models.IntegerField(default=1)
studType = models.CharField(max_length=15)
courseType = models.CharField(max_length=15)
nationality = models.CharField(max_length=20)
VMSAcc = models.CharField(max_length=30)
classGrp = models.ForeignKey('GroupInfo', on_delete=models.SET_NULL, null=True)
class MarkAtt(models.Model):
studName = models.ForeignKey(Namelist,on_delete=models.SET_NULL,blank=True, null=True, default=None)
classGrp = models.ForeignKey(GroupInfo, on_delete=models.SET_NULL, null=True)
currentDate = models.DateField(default=now())
week = models.IntegerField(default=1)
attendance = models.IntegerField(default=100) #1 is present
Thank you in advance.
The below should return a queryset with student names and marks. Two separate queries should not be needed.
students = MarkAtt.objects.values('studName').annotate(mark=Sum('attendance'))
If studName is a foreign key, do this .values('studName__name')
Then in your context you can just add:
context = {
students = students
}
You can then loop through students in you template and display data as:
<p>{{x.studName}}</p> <p> {{x.mark}}</p>
You are overcomplicating things. Given the following simple context:
students = MarkAtt.objects.values('studName').annotate(mark=Sum('attendance'))
context = {
'students' : students,
}
you can do in the template:
{% for student in students %}
<p>{{student.studName}}</p> <p> {{ student.mark}}</p>
{% endfor %}
I have two querysets. My first Queryset (services) has all of my service objects. My second Queryset (rating_values) has all of my rating values, where each index corresponds to the index in the services Queryset. For example services[1] corresponds with rating_values[1].
How do I loop through these two queries in an html page in django so so that I can display the service name along with the corresponding rating value?
To be clear, services contains service objects and rating_values contains decimal values. I pass the variables like this:
return render_to_response('services/display_services.html', {'user': request.user, 'services':services, 'rating_values': rating_values })
I would like to do something like this:
{% for service in services %}
<p> {{service.service_name}} : {{ corresponding rating_value}} </p>
Edit
Here is my Service model:
class Service(models.Model):
user_id= models.IntegerField(default=1)
service_name = models.CharField(max_length=100)
address = models.CharField(max_length=200)
def __str__(self): # __unicode__ on Python 2
return self.service_name
Here is my Rating Model:
class Rating(models.Model):
service_id= models.IntegerField(default=1)
service_name= models.CharField(max_length=200)
number_of_ratings = models.IntegerField()
total_rating_value = models.IntegerField()
rating_average = models.FloatField()
def __str__(self): # __unicode__ on Python 2
return self.service_name
Just to answer the original question as well.
If you for some reason don't want a database relation between the models, you could convert the querysets to lists and zip them in the view, turning the querysets to a list of tuples.
view:
return render_to_response(
'services/display_services.html',
context_instance={
'user': request.user,
'services_and_ratings': zip(
services.all(),
rating_values.all()
)
}
)
template:
{% for service, rating in services_and_ratings %}
<p> {{ service.service_name }} : {{ rating.rating_average }} </p>
You can establish the relationship right in the model.
class Rating(models.Model):
service= models.OneToOneField(Service, primary_key=True)
service_name= models.CharField(max_length=200)
number_of_ratings = models.IntegerField()
total_rating_value = models.IntegerField()
rating_average = models.FloatField()
....
You can then pass services and get the corresponding ratings..
{% for service in services %}
<p> {{service.service_name}} : {{ service.rating.rating_average }} </p>
I have model for question:
class Question(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=120)
description = models.TextField()
answers = models.ManyToManyField('Answer',related_name='answer_name', blank=True)
post_date = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.title
And I have model for answer:
class Answer(models.Model):
user = models.ForeignKey(User)
question = models.ForeignKey(Question)
ans_body = models.TextField()
post_date = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.ans_body
Question creation and answer submission are working perfectly. I cant correctly show answer for particular question. But when I try to get the count of answer for particular question its not showing. It displays 0 count.
In my view I am getting the list of the answer by:
context["question_list"] = Question.objects.all()
And in my template
{% for question in question_list %}
{{ question.title }}
Ans:{{question.answers.count}}
{% endfor %}
When I do this I get the count 0 if there are answers. How can I get the count of the answers for particular questions.
This worked:
{{question.answer_set.count}}
Happy..
You can do something like {{ question.answers.all.count }}, but if you are iterating over more than question it will cause a database query for every question.
If you want to annotate the whole queryset with the count for each question:
from django.db.models import Count
context['question_list'] = Question.objects.all().annotate(
answer_count=Count('answers')
)
Then you can access the count for each question with {{ question.answer_count }}.
Why not use: Question.objects.all().count()
For my project, I have a Field in 'Info' Model
users_like = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="%(app_label)s_%(class)s_likes", blank=True)
I use below code to count the number of like then show it in Admin List Page.
# admin.py
from django.contrib import admin
from .models import Info
class InfoAdmin(admin.ModelAdmin):
list_display = ('id', 'title', 'like_count',)
def like_count(self, obj):
return obj.users_like.all().count()
admin.site.register(Info, InfoAdmin)
The result is:
Hope these can help you!
This will work for you without any problem in your case:
{{question.answer_name.count}}