I have order_by() model django and got queryset following result:
queryset = <QuerySet [<MyModel: MyModel object (4)>, <MyModel: MyModel object (2)>,
<MyModel: MyModel object (1)>, <MyModel: MyModel object (3)>, <MyModel: MyModel object (5)>]>
The result is not sorted by id.
And I want slice queryset that have order MyModel with id greater than 1 to get the following results:
new_queryset = <QuerySet [<MyModel: MyModel object (4)>, <MyModel: MyModel object (2)>]>
Is there any way to slice without loop like this to reduce the query ?
for index in range(len(queryset)):
if queryset[index].id == 1:
new_queryset = queryset[:index]
break
Assuming that you are ordering the queryset based on publish_datetime and modified, you can simply get the record with needed id, and filter the queryset according to the ordering relevant to the object, like this:
from django.db.models import F, Q
reference_object = MyModel.objects.get(id=1)
queryset = MyModel.objects.filter(
Q(publish_datetime__lt=reference_object.publish_datetime) |
Q(
Q(publish_datetime=reference_object.publish_datetime) &
Q(modified__gt=reference_object.modified)
)
)
You will get the results where posts were published before the needed object, or with the same published time, but with more recent modified date
You can make use of filter or exclude functions that the QuerySetManager provides. Basically you can do the following:
new_queryset = MyModel.objects.filter(id__gt=1)
You can slice the list result as shown below:
new_queryset = MyModel.objects.filter(id__gt=1)[:5]
The 5 means the only 5 records would be displayed according to your filtered queryset.
Related
I have encountered some unexpected sorting behaviour with objects. As soon as I sort by the related model field, I get duplicates.
A short description of the model fields
models.py
class GoogleCreativeSpend(models.Model):
creative = models.ForeignKey(
'GoogleCreative',
on_delete=models.CASCADE,
)
spend = models.DecimalField()
class GoogleCreative(CreamCreative):
.....
Create some objects:
>>> creative = GoogleCreative.objects.get(name='gs_video1031v1')
>>> spend = GoogleCreativeSpend(creative=creative, spend=100,)
>>> spend.save()
>>> spend = GoogleCreativeSpend(creative=creative, spend=1100,)
>>> spend.save()
>>> spend = GoogleCreativeSpend(creative=creative, spend=1,)
>>> spend.save()
views.py
queryset = GoogleCreative.objects.all()
queryset.order_by('googlecreativespend__spend')
for i in queryset:
if i.name == 'gs_video1031v1':
print(i.name)
| gs_video1031v1
| gs_video1031v1
| gs_video1031v1
I.e. by creating 3 googlespend objects I get 3 duplicates for googlecreative after sorting.
According to How to sort queryset based on foreign key with no duplicates
I tryied
queryset.distinct()
and
queryset.distinct('googlecreativespend__spend')
But it doesn't work
How to fix it ?
Finde the solution using Avg
queryset = queryset.annotate(spend=Avg('googlecreativespend__spend'))
queryset.order_by('spend')
upd
Be careful, using Avg with values equal to None leads to unpredictable sorting results. The solution is to add a default value
queryset = queryset.annotate(
spend=Coalesce(Avg('googlecreativespend__spend'), 0, output_field=DecimalField())
)
I have an Item class which can be annotated using a custom queryset add_is_favorite_for method:
class ItemQuerySet(QuerySet):
def add_is_favorite_for(self, user):
"""add a boolean to know if the item is favorited by the given user"""
condition = Q(id__in=Item.objects.filter(favoriters=user).values("id"))
return self.annotate(is_favorite=Condition(condition)) # True or False
class Item(Model):
objects = Manager.from_queryset(ItemQuerySet)()
It works as expected. For example:
>>> user = User.objects.get(id=1)
>>> Item.objects.add_is_favorite_for(user) # each item has now a `is_favorite` field
Then, I added a Factory model and link Item model to it using a 1->N relationship:
class Factory(Model):
pass # ...
class Item(Model):
objects = Manager.from_queryset(ItemQuerySet)()
advised_in = models.ForeignKey(
Factory,
on_delete=models.CASCADE,
related_name="advised_items",
)
Now, I'd like to be able to return a Factory QuerySet, whose advised_items fields will all contain the is_favorite annotation too.
I don't know how to do this, I saw no example of such a thing in the doc, maybe I missed it.
You can work with a Prefetch object [Django-doc]:
from django.db.models import Prefetch
Factory.objects.prefetch_related(
Prefetch('advised_items', queryset=Item.objects.add_is_favorite_for(some_user))
)
Schema: tables A and B with M2M between them.
I have some queryset QS0 of A objects and one exact instance of B object.
How to annotate QS0 with True if B is connected with A through M2M and False if it is not?
Thanks
Given the example models below
from django.db import models
class ModelA(models.Model):
title = models.CharField(max_length=100)
class ModelB(models.Model):
title = models.CharField(max_length=100)
a_objects = models.ManyToManyField(ModelA, related_name='b_objects')
You should in theory be able to annotate a queryset of ModelA objects on whether they are each linked to a ModelB object in the following way.
from django.db.models import Case, When, Value
b_object = ModelB.objects.get(id=some_id)
QS0 = ModelA.objects.annotate(is_linked_to_b=Case(When(b_objects__id=b_object, then=Value(True)), default=Value(False), output_field=BooleanField())
# QSO[some_index].is_linked_to_b should return either True or False.
bar_1 = models.IntegerField()
bar_2 = models.IntegerField()
Filter all values where bar_1<=bar_2
You can filter with an F-object [Django-doc]:
from django.db.models import F
MyModel.objects.filter(bar_1__lte=F('bar_2'))
One uses an F-object to refer to a field. So F('bar_2') represents the bar_2 field.
If I have models like so:
class A(models.Model):
...
class B(models.Model):
a = models.ForeignKey(A)
class C(models.Model):
b = models..ForeignKey(B)
I can get the full queryset:
qs = A.objects.all().prefetch_related('b_set', 'b_set__c_set')
>> <QuerySet [<A: A object (1)>, <A: A object (2)>, ... ]>
What I want to know is there a way to get all the C objects in a flattened queryset, e.g:
qs['b_set__c_set']
>> <QuerySet [<C: C object (1)>, <C: C object (2)>, ... ]>
You could do a query on the C Model, just checking for all C's that have an a value? So
C.objects.filter(b__is_null=False, b__a__isnull=False)
Would that achieve what you're looking for?