Retrieve a flat Queryset of prefetch_related objects from a parent queryset - python

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?

Related

Slice queryset after order_by() in Django

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.

Annotate query by existance in M2M

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.

Django extract queryset from ManyToMany with through field

Say we have those models:
class A(models.Model):
field = models.ManyToManyField(B, through="C")
class B(models.Model):
value = models.CharField()
class C(models.Model):
a = models.ForeignKey(A)
b = models.ForeignKey(B)
order = models.IntegerField()
Is there an option to extract queryset of B's, but taking into consideration order field?
Doing a a.c_set.all() returns queryset for C class (but it's ordered).
Doing a a.fields.all() works, but the queryset is unordered.
I need a queryset for initializing the formset.
I hope it's understandable - it's quite late and i can't think clearly already... I'll try to clear it out if anyone has any questions.
Thanks in advance
If you put a an ordering on model C, all queryset on C would obey that order:
class C(models.Model):
class Meta:
ordering = ('order', )
Now if you want B objects related to A, you could sort the Bs based on C's ordering:
b_results = a.fields.order_by('c')
Or if the order_by('c') is not clear enough, you could change your model to be:
class C(models.Model):
a = models.ForeignKey(A, related_name='a_relationship')
b = models.ForeignKey(B)
order = models.IntegerField()
class Meta:
ordering = ('order', )
Then you could do:
b_results = a.fields.order_by('a_relationship')
Use the C model reverse relations to do the order, e.g.
a.fields.order_by(c__order)

Django inheritance and subclass attributes

With a configuration such as this:
class A (models.Model):
common_attribute = models.IntegerField()
class B (A):
subclass_attribute = models.IntegerField()
class C (models.Model)
a = ForeignKey(A)
... if an instance of C contains an instance of B, Django seems to treat this as an instance of A and I can't access c.a.subclass_attribute. Is there a way around this? I don't want to use abstract inheritance because of the difficulties with ForeignKeys - I want C to support any subclass of A.
You can circumvent the difficulties with ForeignKey by using django-polymorphic.
Django Polymorphic allows you to query the base class objects but retrieves the child class instances:
>>> Project.objects.create(topic="Department Party")
>>> ArtProject.objects.create(topic="Painting with Tim", artist="T. Turner")
>>> ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")
>>> Project.objects.all()
[ <Project: id 1, topic "Department Party">,
<ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
<ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
To use django polymorphic you only need to declare your models with Polymorphic Model as base class:
from django.db import models
from polymorphic import PolymorphicModel
class ModelA(PolymorphicModel):
field1 = models.CharField(max_length=10)
class ModelB(ModelA):
field2 = models.CharField(max_length=10)
class ModelC(ModelB):
field3 = models.CharField(max_length=10)
Foreign keys will also return the child class instances, which I think is exactly what you want.
# The model holding the relation may be any kind of model, polymorphic or not
class RelatingModel(models.Model):
many2many = models.ManyToManyField('ModelA') # ManyToMany relation to a polymorphic model
>>> o=RelatingModel.objects.create()
>>> o.many2many.add(ModelA.objects.get(id=1))
>>> o.many2many.add(ModelB.objects.get(id=2))
>>> o.many2many.add(ModelC.objects.get(id=3))
>>> o.many2many.all()
[ <ModelA: id 1, field1 (CharField)>,
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
Take into account that these queries will be slightly less performant.

Django Releationship between multiple fields

I just need a little help thinking through this, if someone could be so kind.
class object_a(models.Model):
foo = models.blah
bar = models.blah
class object_b(models.Model):
foop = models.blah
barp = models.blah
In another model I have a class that I want to have a single relationship with both fields. For example, in the admin I want a list of both object_a and object_b objects selectable in some sort of relationship.
I know a generic relationship of some sort can do this, I just can't quite get to the end of the though. all help is appreciated.
Thanks.
Use the contenttypes framework provided by Django:
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
class Other(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
Then you can create some of your main objects:
>>> from myapp.models import object_a, object_b, Other
>>> a = object_a()
>>> a.foo = 'test'
>>> a.bar = 'value'
>>> a.save()
>>> b = object_b()
>>> b.foop = 'random'
>>> b.barp = 'values'
>>> b.save()
And then save references to them in Other objects:
>>> o1 = Other()
>>> o1.content_object = a
>>> o1.save()
>>> o2 = Other()
>>> o2.content_object = b
>>> o2.save()
Now if we ask for all the Other objects and inspect them:
>>> all = Other.objects.all()
>>> all[0].content_object
<object_a: object_a object>
>>> all[0].content_object.foo
u'test'
>>> all[1].content_object
<object_b: object_b object>
>>> all[1].content_object.foop
u'random'
By looking at the fields on the Other object, we can see how Django stores the generic relations:
>>> all[0].content_type
<ContentType: object_a>
>>> all[0].object_id
1
When you run the syncdb command to install your models, Django creates a unique ContentType object for each model. When you use generic relations, you store a foreign key to this content type entry (identifying what type of object is at the other end of the relation), and the unique ID of the model instance. The generic.GenericForeignKey field is not stored in the database, but is a wrapper which takes the content type and object ID and retrieves the actual object for you.

Categories