I would like to sort my Docket object based on the most recent Document (a child object). There are typically several documents with the same date_filed. Based on the tutorial, I tried sorting on -documents__date_filed and it sort of works. Because there are multiple Documents with the exact same date, my ListView shows 2-3 repeated rows (one for each date tie). How do I avoid getting the duplicated results and just have one row for each Docket in the output?
class Docket(models.Model):
name = models.CharField(max_length=200)
class Meta:
ordering = ['-documents__date_filed', Func(F('name'), function='LOWER')]
class Document(models.Model):
docket = models.ForeignKey(Docket, on_delete=models.CASCADE, related_name="documents")
date_filed = models.DateTimeField(default=timezone.now)
It might be better to do the ordering in the ListView itself if it includes a related model, since now nearly all Docker.objects.all() querysets will have duplicates.
You can make use of .distinct() [Django-doc] to retrieve a queryset of Docket objects where the same one only occurs once:
from django.views.generic import ListView
from django.db.models.functions import Lower
class DocketListView(ListView):
model = Docket
queryset = Docket.objects.order_by(
'-documents__date_filed',
Lower('name')
).distinct()
Related
I have a Django database model that has some attributes, one of them is a Charfield 'category' with Choices.
I now want to annotate a queryset of this model with the count of the rows of each category. The thing is, the way i know to do it, only categories present in the queryset are getting counted, but i want a queryset with all categories annotated with the count (0 if no rows with this category).
This is what im doing at the moment:
Model.objects.all().values('category').annotate(total=Count('category'))
Is there a way to display all categories, including such with count 0?
You can not count categories that do not exist, since, well... these do not exist. The choices are not even transferred to the database.
Using a CharField with choices is not the ideal modeling for this. Typically it is better to make a model Category and then another MyModel with ForeignKey [Django-doc] to link a MyModel object to a Category, so:
class Category(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class MyModel(models.Model):
category = models.ForeignKey(Category, on_delete=models.PROTECT)
then we can create categories like:
Category.objects.bulk_create(
Category(name='technology'),
Category(name='art'),
Category(name='science')
)
if we then link MyModel objects to these Categorys, we can annotate the Category with the number of MyModels with:
from django.db.models import Count
Category.objects.annotate(
num_mymodel=Count('mymodel')
)
Here the Categorys that arise from this queryset will have an extra attribute .num_mymodel with the number of linked MyModel objects. Since a LEFT OUTER JOIN is performed, for Categorys without any MyModels, it will use 0.
I'm using Django and Python 3.7. I have these two models. Note they both have a similar field
class Article(models.Model):
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE,)
class WebPageStat(models.Model):
objects = WebPageStatManager()
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE, )
score = models.BigIntegerField(default=0)
I would like to write a Django ORM query that selects one of the models but does a join on the shared column in order to do a comparison on the "score" field. I tried the below, but alas the error
Article.objects.filter(publisher=webpagestat.publisher, webpagestat.score__gte==100)
File "<input>", line 1
SyntaxError: positional argument follows keyword argument
How do I include the "WebPageStat" table in my join?
What you want is easily done in Django by using lookups that span relations. In your example you need to add a way of referencing the related objects in the ORM. This is donde by setting the related_name attribute of the ForeignKey fields. Although this is not strictly necessary it is more clear in this way. You can read more about related_name and related_query_name here. In short, your models should look like:
class Article(models.Model):
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE, related_name='articles')
class WebPageStat(models.Model):
objects = WebPageStatManager()
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE, related_name='stats')
score = models.BigIntegerField(default=0)
Once this is done your query will be carried out by the following code:
Article.objects.filter(publisher__stats__score__gte=100)
What this query is doing in short is selecting every article whose publisher has at least one web page stat whose score is greater than or equal to 100.
Assuming I have two models:
from django.db import models
class Parent(models.Model):
pass
class Child(models.Model):
parent = models.ForeignKey(Parent,
on_delete=models.CASCADE,
related_name='children')
active = models.BooleanField()
How might I go about getting a query set of parents that have at least 1 active child? In other words, how would I get a query set of parents that excludes those without any active children? If using model properties in filters was feasible, this would be a trivial task, but that is not possible. This is also a simple operation using list comprehensions, but it's important here for the queryset to be the end result.
One solution is to use aggregation: https://docs.djangoproject.com/en/2.1/topics/db/aggregation/
We can annotate a queryset of parents with the number of active children they have, and then filter against that queryset to only find parents with a number greater than or equal to 0:
from django.db.models import Count, Q
num_active_children = Count('children', filter=Q(children__active=True))
parents_with_any_active_children = (Parent.objects
.annotate(num_active_children=num_active_children)
.filter(num_active_children__gte=1)
)
Parent.objects.filter(child__active=True)
Hope this is enough for you.
I have 2 models:
class Listing:
title = models.CharField()
class Location(models.Model):
listing = models.ForeignKey(Listing)
Given a set of locations, l = Location.objects.all()
I want to find l.listing (Which is not the correct command)
Something like listings = Listing.objects.filter(listing_set_in=l).
It has to be as efficient as possible.
If I understand you correctly, you need to find all items from Listing, that present in l queryset. To do so, you can filter Listing queryset by values from l.
You can use Django subquery to reduce amount of queries.
w/ Subquery
Listing.objects.filter(pk__in=l.values('listing_id'))
The same with Subquery
from django.db.models import Subquery
Listing.objects.filter(pk__in=Subquery(l.values('listing_id')))
How can prefetch_related and values method be applied in combination?
Previously, I had the following code. Limiting fields in this query is required for performance optimization.
Organizations.objects.values('id','name').order_by('name')
Now, I need to prefetch its association and append it in the serializer using "prefetch_related" method.
Organizations.objects.prefetch_related('locations').order_by('name')
Here, I cannot seem to find a way to limit the fields after using "prefetch_related".
I have tried the following, but on doing so serializer does not see the associated "locations".
Organizations.objects.prefetch_related('locations').values("id", "name").order_by('name')
Model Skeleton:
class Organizations(models.Model):
name = models.CharField(max_length=40)
class Location(models.Model):
name = models.CharField(max_length=50)
organization = models.ForeignKey(Organizations, to_field="name", db_column="organization_name", related_name='locations')
class Meta:
db_table = u'locations'
Use only() to limit number of fields retrieved if you're concerned about your app performances. See reference.
In the example above, this would be:
Organizations.objects.prefetch_related('locations').only('id', 'name').order_by('name')
which would result in two queries:
SELECT id, name FROM organizations;
SELECT * from locations WHERE organization_name = <whatever is seen before>;