prefetch_related with nested m2m - django

Given the following models I would like to improve the performance of the filter to avoid unnecessary calls to DB.
class A(models.Model):
name = models.CharField()
b = ManyToManyField(B)
class B(models.Model):
c = ManyToManyField(C)
class C(models.Model):
d = ManyToManyField(D)
class D(models.Model):
foo = models.TextField()
How could I achieve this with prefetch_related?
''.join(A.objects.filter(b__c__d=self.d).prefetch_related('??').values_list('name', flat=True))

It works in a similar fashion to your filter (b__c__d). Just prefetch:
''.join(A.objects.filter(b__c__d=self.d).prefetch_related('b__c__d').values_list('name', flat=True))

Related

Is it possible to prefetch model with one query in this case?

Is it possible to prefetch Models B to Model A with one query or with minimal queries. I'm confused. Thanks.
from django.db import models
class ModelA(models.Model):
pass
class ModelB(models.Model):
pass
class ModelC(models.Model):
model_a = models.ForeignKey(ModelA, related_name="models_a", on_delete=models.CASCADE)
models_b = models.ManyToMany(ModelB, through="ModelD")
class ModelD(models.Model):
model_c = models.ForeignKey(ModelC, on_delete=models.CASCADE)
model_b = models.ForeignKey(ModelB, on_delete=models.CASCADE)
I'm do something like that, and it is work. But seems a bit ugly.
models_a_list = ModelsA.objects.all()
model_d_qs = ModelD.objects.select_related("model_c", "model_b")
model_d_map = defaultdict(list)
for d_item in model_d_qs:
model_d_map[d_item.model_c.model_a.id].append(d_item.model_b)
for a_item in models_a_list:
settatr(a_item, "model_b_set", model_d_map.get(a_item.id))
return models_a_list

Common fields for different django models in one place

I have some columns that are repeated in multiple models. is there any solution to place them somewhere and use it any model?
You can achieve this by creating base classes and inheriting them in your models.
Example:
class TimestampsModel(models.Model):
#classmethod
def get_fields(cls, fields: tuple):
return fields.__add__(('created_at', 'updated_at'))
created_at = models.DateTimeField(("created_at"), auto_now_add=True)
updated_at = models.DateTimeField(("updated_at"), auto_now=True)
You can also make it abstract and Django won't create migrations for this.
class Meta:
abstract = True
Finally, a model would be:
class Blog(baseModels.TimestampsModel):
class Meta:
db_table = "blog"
title = models.CharField(max_length=100)

Count how many fields a model has

I am wondering if there is a way to know how many fields a model contains.
Example:
class Post(models.Model):
title = models.TextField()
body = models.TextField()
sub_title = models.TextField()
summary = models.TextField()
I can count it but I would like to know if there is an in-built method that allows me to do so.
Ideally the quesry/code would be:
Post.number_of_fieds-->output--> 4
Does such a thing exist?
To the best of my knowledge, there is no builtin, you can obtain the fields with:
>>> len(Post._meta.fields)
5
We can define that on a BaseModel class and subclass this, to make such function available to all subclasses:
class BaseModel(models.Model):
class Meta:
abstract = True
#classmethod
def number_of_fields(cls):
return len(cls._meta.fields)
class Post(BaseModel):
title = models.TextField()
body = models.TextField()
sub_title = models.TextField()
summary = models.TextField()
The .fields return an ImmutableList of fields defined on that model. We can use .get_fields() to take into account the relations that are targetting that model as well.
Then we can query like:
>>> Post.number_of_fields()
5
Note however that this will return 5, since the model has an (implicit) primary key here.
You can try this
>>> Post._meta.fields.__len__()
5

Is there some way to extend the created model fields another model in django?

Say, I want to create two models, out of which for the all the fields for the second model will remain same as the first model, with addition of a few more fields. Is there a good way to achieve this?
One way is to use abstract class as the first model, but this will mean i wont be able to add instances of the first model. You can find examples in the code below:
Say we have two models as:
class Model1(models.Model):
m1field1 = models.CharField(max_length=255,required=True)
m1field2 = models.CharField(...)
m1field3 = models.CharField(...)
...
m1field8 = models.CharField(...)
class Model2(models.Model):
m1field1 = models.CharField(max_length=255,required=True)
...
m1field8 = models.CharField(...)
m2field1 = models.CharField(...)
...
m2field5 = models.CharField(...)
As you can see I have to redefine the m1field1 - m1field8 again in the Model2, if I have to reuse it.
Is there some way, to not repeat it again.
I tried the following:
class Model2(Model1):
m2field1 = models.CharField(...)
...
m2field5 = models.CharField(...)
But, this didnt work as in the hindsight, it creates a OneToOneField of Model2 to Model1, which obviously wont work, as I will have to first create a Model1 with the given fields and then Model2 with the fields and then link it to Model1.
One probable solution is to use abstract model, with the common fields. But is there some other method to achieve the same results?
You can create mixin for common fields like this.
class CommonFieldsMixin(models.Model):
m1field1 = models.CharField(max_length=255,required=True)
m1field2 = models.CharField(...)
m1field3 = models.CharField(...)
...
m1field8 = models.CharField(...)
class Meta:
abstract = True
Use this mixin in both models.
class Model1(CommonFieldsMixin, models.Model):
pass
class Model2(CommonFieldsMixin, models.Model):
m2field1 = models.CharField(...)
m2field5 = models.CharField(...)
You can try like this:
class ModelBase(models.Model):
# declare a base abstract class
m1field1 = models.CharField(max_length=255,required=True)
m1field2 = models.CharField(...)
m1field3 = models.CharField(...)
...
m1field8 = models.CharField(...)
class Meta:
abstract = True
class Model1(ModelBase):
# subclass from abstract class
pass
class Model2(ModelBase):
# subclass from abstract class
m2field1 = models.CharField(...)
...
m2field5 = models.CharField(...)

Django: select_related to a table vs table's field

I have 2 models
class A(models.Model):
val = models.IntegerField()
class B(models.Model):
val2 = models.IntegerField()
a = models.ForeignKey(A)
class C(models.Model):
b = models.ForeignKey(B)
val3 = models.IntegerField()
How is the query -
C.objects.select_related('B').all()
better than -
C.objects.select_related('B__val2').all()
And if not how query one can be optimised?
Try to filter model you need, by lowcase filter of the child models
B.objects.filter(c__isnull=False)
read more here lookups-that-span-relationships