I'm trying to get an item with the most occurrences of a foreign key (votes), inside of a queryset (Questions, Choices).
I.E. I need to get the most popular vote to set the 'winner' attribute in the JsonResponse.
Any help on how I can figure this out?
Here is my view.
allchoices = [{
'id':i.id,
'question':i.question.id,
'choice_text':i.choice_text,
'votes':i.voter_set.all().count()
} for i in poll_choices]
return JsonResponse({
"votes":choice.voter_set.all().count(),
'winner':True,
'success':True,
'allchoices':allchoices
},safe=False,)
These are my models:
class Voter(models.Model):
# Authentication of anonymous users
choice = models.ForeignKey('PollChoice', on_delete=models.CASCADE)
question = models.ForeignKey('PollQuestion', on_delete=models.CASCADE)
class PollChoice(models.Model):
question = models.ForeignKey('PollQuestion', on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
def __str__(self):
return self.choice_text
class PollQuestion(models.Model):
question = models.CharField(max_length=200)
created = models.DateTimeField(auto_now_add=True)
creator = models.ForeignKey('poll_creator',/\
on_delete=models.CASCADE, null=False, blank=False)
uuid = ShortUUIDField(length=8, max_length=12)
def poll_choices(self):
return self.pollchoice_set.all().annotate(voters=/\
models.Count(models.F('voter'))).order_by('-voters')
def choices(self):
return self.pollchoice_set.all()
def __str__(self):
return f'{self.question}'
You can order by the number of related Voters, with:
from django.db.models import Count
poll_choice = PollChoice.objects.alias(
num_voters=Count('voter')
).latest('num_voters')
this will retrieve the PollChoice with the largest amount of related Voters. You can filter the PollChoice further, for example to only consider PollChoices for a certain PollQuestion with:
from django.db.models import Count
poll_choice = PollChoice.objects.filter(question=my_question).alias(
num_voters=Count('voter')
).latest('num_voters')
Related
so,i have a List of all mobile phones whose brand name is one of the incoming brands. The desired brand names will be entered. The number of entries is unknown and may be empty. If the input is empty, the list of all mobile phones must be returned.
model:
from django.db import models
class Brand(models.Model):
name = models.CharField(max_length=32)
nationality = models.CharField(max_length=32)
def __str__(self):
return self.name
class Mobile(models.Model):
brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
model = models.CharField(max_length=32, default='9T Pro', unique=True)
price = models.PositiveIntegerField(default=2097152)
color = models.CharField(max_length=16, default='Black')
display_size = models.SmallIntegerField(default=4)
is_available = models.BooleanField(default=True)
made_in = models.CharField(max_length=20, default='China')
def __str__(self):
return '{} {}'.format(self.brand.name, self.model)
query:
from django.db.models import F, Q
def some_brand_mobiles(*brand_names):
query = Mobile.objects.filter(Q(brand__name__in=brand_names) | ~Q(brand__name=[]))
return query
If the input is empty, the list of all mobile phones will be returned, but i cant use *brand_names to return the list.
for example
query = Mobile.objects.filter(Q(brand_name_in=['Apple', 'Xiaomi']))
return query
and
query = Mobile.objects.filter(~Q(brand__name=[]))
return query
Both of these conditions work by example alone, but it does not check both conditions with the function I wrote.
how to fix it ?
It is simpler to just check the list of brand_names and filter if it contains at least one element:
def some_brand_mobiles(*brand_names):
if brand_names:
return Mobile.objects.filter(brand__name__in=brand_names)
else:
return Mobile.objects.all()
Try this solution:
def some_brand_mobiles(*brand_names):
queryset = Mobile.objects.all()
if brand_names:
queryset = queryset.filter(brand__name__in=brand_names)
return queryset
In that way you can add more filters based on any other condition over the queryset.
I have these models:
Organisation
Student
Course
Enrollment
A Student belongs to an Organisation
A Student can enrol on 1 or more courses
So an Enrollment record basically consists of a given Course and a given Student
from django.db import models
from model_utils.models import TimeStampedModel
class Organisation(TimeStampedModel):
objects = models.Manager()
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Student(TimeStampedModel):
objects = models.Manager()
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField(unique=True)
organisation = models.ForeignKey(to=Organisation, on_delete=models.SET_NULL, default=None, null=True)
def __str__(self):
return self.email
class Course(TimeStampedModel):
objects = models.Manager()
language = models.CharField(max_length=30)
level = models.CharField(max_length=2)
def __str__(self):
return self.language + ' ' + self.level
class Meta:
unique_together = ("language", "level")
class EnrollmentManager(models.Manager):
def org_students_enrolled(self, organisation):
return self.filter(student__organisation__name=organisation).all()
class Enrollment(TimeStampedModel):
objects = EnrollmentManager()
course = models.ForeignKey(to=Course, on_delete=models.CASCADE, default=None, null=False, related_name='enrollments')
student = models.ForeignKey(to=Student, on_delete=models.CASCADE, default=None, null=False, related_name='enrollments')
enrolled = models.DateTimeField()
last_booking = models.DateTimeField()
credits_total = models.SmallIntegerField(default=10)
credits_balance = models.DecimalField(max_digits=5, decimal_places=2)
Notice the custom EnrollmentManager that allows me to find all students who are enrolled from a given organisation.
How can I add a custom Manager to retrieve all the courses from a given organisation whose students are enrolled?
What I have tried
I thought to create a CourseManager and somehow query/filter from that side of the relationship:
class CourseManager(models.Manager):
def org_courses_enrolled(self, organisation):
return self.filter(enrollment__student__organisation__name=organisation).all()
This works, but it gives me the same 100 enrollment records :(
What I am trying to get is:
based on a given organisation
find all students who are enrolled
and then (DISTINCT?) to get the list of enrolled courses for that org
This is the view:
class OrganisationCoursesView(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
serializer_class = CourseSerializer
queryset = Course.objects.get_courses(1)
and the url:
# The below should allow: /api/v1/organisations/1/courses/
router.register('api/v1/organisations/(?P<organisation_pk>\d+)/courses', OrganisationCoursesView, 'organisation courses')
UPDATE 1
Based on the answer from h1dd3n I tried this next:
class CourseManager(models.Manager):
def get_queryset(self):
return super(CourseManager, self).get_queryset()
def get_courses(self, organisation):
return self.get_queryset().filter(student__organisation_id=organisation)
but that throws an error (as I expected it would):
FieldError: Cannot resolve keyword 'student' into field. Choices are:
courses, created, id, language, level, modified, progress
UPDATE 2 - getting closer!
Ok with help from #AKX's comments:
class CourseManager(models.Manager):
def get_queryset(self):
return super(CourseManager, self).get_queryset()
def get_courses(self, organisation):
return self.get_queryset().filter(courses__student__organisation_id=organisation)
now DOES return courses, but it returns a copy for each enrolled student. So now I need to group them so each record only appears one time...
First you need to change self.filter to self.get_queryset().filter() or make a seperate method in the manager.
def get_queryset(self):
return super(CourseManager, self).get_queryset()
In manager create a function
def get_courses(self,organisation):
return self.get_queryset.filter(student__oraganisation=organisation)
This should return the students and you don't need to call .all() - the filtered qs either way returns you all the objects that it finds.
EDIT
Try this:
class CourseManager(models.Manager):
def get_queryset(self):
return super(CourseManager, self).get_queryset()
def get_courses(self, organisation):
return self.get_queryset().filter( \
enrollments__student__organisation_id=organisation).distinct()
UPDATE 2
You can try and play around with from django.db.models import Q https://docs.djangoproject.com/en/3.0/topics/db/queries/
or with annotate based on this answer Get distinct values of Queryset by field where you filter out each student so it would appear once.
I'm fairly new to Django and I'm in need of assistance with my models.
class Region(models.Model):
region_name = models.CharField(max_length=10)
def __str__(self):
return self.region_name
class Property(models.Model):
prop_name = models.CharField(max_length=200)
region_name = models.ForeignKey(Region, on_delete=models.CASCADE, verbose_name="Region")
prop_code = models.IntegerField(default=0, verbose_name="Property")
def __str__(self):
return self.prop_name
class Sale(models.Model):
prop_name = models.ForeignKey(Property, on_delete=models.CASCADE)
employee = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="Person")
prop_state = models.CharField(null=True, max_length=5, choices=[('new','New'),('used','Used')])
date = models.DateField('Sale Date')
def __str__(self):
return '%s : %s %s - %s' % (self.prop_name.prop_name, self.employee, self.date, self.prop_state)
Here are my models. Property inherits from Region and Sale inherits from property. What I want to do is count the number of sales in a region and the number of sales on a specific property. However I do not know which would be the best way to approach this. I've tried using a lambda as a model field that uses the count() function but I wasn't able to see much success with that. Please let me know if you have any suggestions.
If you already have your Property/Region objects, something like this should work
sales_per_property = Sale.objects.filter(prop_name=property).count()
sales_per_region = Sale.objects.filter(prop_name__region_name=region).count()
Edit:
Seeing that you tried to add a lambda function to the model field, this may be more what you are looking for.
class Region(models.Model):
...
#property
def sales(self):
return Sale.objects.filter(prop_name__region_name=self).count()
and similarly for Property. Simply access the property using region.sales
You can annotate your querysets for Region and Property. For example:
from django.db.models import Count
regions = Region.objects.annotate(sales=Count('property__sale'))
properties = Property.objects.annotate(sales=Count('sale'))
The Regions/Propertys that arise from these querysets will have an extra attribute .sales that contains the number of related Sale objects.
In my postgressql database I have tables:
class Topic(models.Model):
Definition = models.TextField(default='Definition')
Name = models.TextField(default='Name')
def __str__(self):
return self.Name
class Question(models.Model):
Statement = models.TextField(default='Question')
def __str__(self):
return self.Statement
class Planit_location(models.Model):
Planit_location = models.CharField(max_length=255, default='Planit_location')
def __str__(self):
return self.Planit_location
class ClientDetail(models.Model):
Sector = models.ForeignKey(Sector, on_delete=models.CASCADE)
Client_name = models.CharField(max_length=255, default='Client_name')
def __str__(self):
return self.Client_name
class Response(models.Model):
Question = models.ForeignKey(Question, on_delete=models.CASCADE)
Topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
Response = models.TextField(default='Response')
Client = models.ForeignKey(ClientDetail, on_delete=models.CASCADE)
Planit_Location = models.ForeignKey(Planit_location, on_delete=models.CASCADE)
Image = models.ForeignKey(Image, on_delete=models.CASCADE)
def __str__(self):
return self.Response
I want to create a modelform using all these tables so I can add new questions and responses to my database, which are then linked to a topic, location and client (these 3 will be a dropdownlist from data in db).
I have managed to create a modelform for just question and response but when I try to submit it I get "null value in column "Question_id" violates not-null constraint"
Here is the code:
if request.method == 'POST':
qform = QuestionForm(request.POST)
rform = ResponseForm(request.POST)
if qform.is_valid() and rform.is_valid():
qf = qform.save()
rf = rform.save()
return render(request, 'app/adddatatest.html', {
"qform": QuestionForm(),
"rform": ResponseForm(),
})
After checking for is_valid() in view do this:
qf = qform.save() # Goes to database
rf = rform.save(commit=False) # Doesn't goes to database
rf.Question = qf # gets required attribute
rf.save() # then goes to database
You can't save Response object without specifying the the foreign key Question. So in rform.save pass argument commit=False to not actually saving it in database yet. Then add the value for the foreign key to the new created Response object, foreign key is required otherwise you will get IntegrityError. Then finally save it to the database.
How can I can use managers to query my foreign key and then retrieve objects that are connected to foreign key?
Here is my models.py:
from django.db import models
# Create your models here.
class BookManager(models.Manager):
def title_count(self,keyword):
return self.filter(title__icontains=keyword).count()
class CategoryManager(models.Manager):
def category_count(self):
return self.filter(category__icontains=python).count()
class Category(models.Model):
title=models.CharField(max_length=20)
def __str__(self):
return self.title
class Enquiry(models.Model):
title=models.CharField(max_length=200)
category=models.ForeignKey(Category ,default=False,blank=False)
detail=models.TextField()
objects = BookManager()
objects=CategoryManager()
# tags=models.ChoiceField()
def __str__(self):
return self.title
I tried to use category manager but it gave me a strange error.
I just want to know how exactly we can get the objects that are connected with category foriegn-key and show them as list to the users.
You can combine both the title_count and category_count methods under one Manager. When filtering through a ForeignKey, your field lookup needs to specify the name of the field you're trying to filter on, else it will assume you're querying the ID field. So instead of category__icontains, you would do category__title__icontains.
Another note, in newer versions of Django, you are required to specify the on_delete parameter when defining a ForeignKey.
This is a working example of what I think you're trying to accomplish.
class BookManager(models.Manager):
def title_count(self, keyword):
return self.filter(title__icontains=keyword).count()
def category_count(self, keyword):
return self.filter(category__title__icontains=keyword).count()
class Category(models.Model):
title = models.CharField(max_length=20)
def __str__(self):
return self.title
class Enquiry(models.Model):
title = models.CharField(max_length=200)
category = models.ForeignKey(Category,
default=False,
blank=False,
on_delete=models.CASCADE)
detail = models.TextField()
books = BookManager()
# tags=models.ChoiceField()
def __str__(self):
return self.title
Here's how you would use it:
Enquiry.books.category_count('python')
Enquiry.books.title_count('test')