How I can get attribute from queryset using prefetch_related in Django? - django

I have following model and extracted queryset using prefetch_related as below.
queryset = Light.objects.filter(
certificate__name="A").prefetch_related('zone__namingzone'
)
From this queryset, I want to get following data set.
{"naming1":lpd1,"naming2":lpd2...}
However, when I try to extract attribute from queryset as below, I get create_reverse_many_to_one_manager
for i in queryset:
print (i.zone.namingzone)
What I want to get is naming attribute in naming table. Could anyone tell me how I can extract this?
models.py
class Certificate(models.Model):
name=models.CharField(max_length=20)
class Zone(models.Model):
zone=models.CharField(max_length=20)
class Light(models.Model):
certificate=models.ForeignKey(Certificate, on_delete=models.CASCADE,related_name='certificate')
zone=models.ForeignKey(Zone, on_delete=models.CASCADE,related_name='lightzone')
lpd=models.IntegerField()
class Meta:
unique_together = (('certificate', 'zone'),)
class Naming(models.Model):
zone=models.ForeignKey(Zone, on_delete=models.CASCADE,related_name='namingzone')
naming=models.CharField(max_length=20)

When you traverse a FK in reverse, you end up with a manager, and multiple items on the other side. So i.zone.namingzone in your for loop is a manager, not a NamingZone. If you change your print loop to:
for i in queryset:
print (i.zone.namingzone.all())
You should see all the naming zones for your item. You can extract the naming field from each NamingZone from the queryset as follows:
queryset.values('zone__namingzone__naming')
You probably want to extract a few other fields from your Light model, like the lpd for instance:
queryset.values('lpd', 'zone__namingzone__naming')
You might have a same ldp several times, as many times as it has naming zones.

Related

Create custom Values method in django

I want to create a method semi to values method in Django QuerySet.
The values method problems are:
Miss order of fields in querySet if I make myquery = MyModel.objects.values('field1','field2','field3')
when I print querSet it give me [{'field2':'data','field1':'data','field3':'data'},...]. so this miss order will cause a problem at Union of queryset.
if the field is choices at model then values(...) will give me the key of dictionary instead of its value.
I want to create my custom values method using django Manager
class MyModelManager(models.Manager):
def values(self, *fields):
# I want to get model belong to this manger
# then I want to check the fields if its in models (we have fields came from annotate)
# after that I want to exclude to the fields that has choices
# next I want to call the super values method and passed to it the values that does not have choices
# finally I want to reorder query according to passed fields
class MyModel(models.model):
# An example model
objects = MyModelManager()

Fetch multiple rows from multiple models Django

I have this project where, I need to fetch multiple objects from multiple models in a view. I could do that by for loop but I think I shouldn't hit database in each loop. Should I use prefetch_related. or Should I know some other way to retrieve them.
for example:
class A(models.Model):
title_name=models.CharField(...)
id=models.AutoField(pk=True)
class B(models.Model):
user=models.ForeignKey(User,models.ON_CASCADE=True)
user_status=models.CharField(...)
id=models.ForeignKey(A, models.ON_CASCADE=True)
I need to display user_status, user and associated title_name. I get multiple objects, select_related will not be useful. Any suggestions.
You need make this queryset:
B.objects.all().select_related('user', 'id')
This queryset will generate sql that join user and A data from db
Next, in the model B to make a property:
#property
def title_name(self):
return self.id.title_name
Finally you'll get queryset that makes one SQL request to database and returns all data you need.
By the way, I would rename attribute "id" in the model B to "a".

How can I update two models in one serializer in Django Rest Framework?

I have a database schema that has each object of a certain type being stored across two separate tables (one row in each table, different data in each, with a foreign key from one to the other.)
Unfortunately, Django Rest Framework tends to assume that there is a one to one correspondence between serializers and models, which is not true of my case. How should I be approaching this? It seems like the serializer should return the representation of the object which will be the actual HTTP response of the ajax requests, so using two serializers doesn't seem right. I've looked at extending BaseSerializer (which is how I currently plan to implement this if I don't find better solutions), but certain methods take in an instance, which should contain all the data needed to serialize the object, whereas I have two instances relevant.
Any advice would be super appreciated! Thank you.
Writable nested representations section might help you.
You have 2 models ModelA and ModelB. Create your first model's serializer
class ModelASerializer(serializers.ModelSerializer):
class Meta:
model = ModelA
fields = ('fields',..) #
Then in other model's serializer add the first serializer and override the required methods (like create, update). Something like this:
class ModelBSerializer(serializers.ModelSerializer):
# add the serializer for the foreignkey model
model_a = ModelASerializer()
class Meta:
model = ModelB
fields = ('fields',..) #
def create(self, validated_data):
modela_data = validated_data.pop('model_a')
model_b = ModelB.objects.create(**validated_data)
ModelA.objects.create(model_b=model_b, **modela_data)
return model_b
# override update too ..

How to prefetch aggregated #property in Django?

We have two models (simplified versions):
class Contestant(models.Model):
email = models.EmailField(max_length=255, unique=True)
# plus some other fields
#property
def total_points(self):
return self.points.aggregate(total=Sum('value'))['total'] or 0
class Points(models.Model):
contestant = models.ForeignKey(Contestant, related_name='points')
value = models.PositiveIntegerField()
# plus some other fields which determine based on what we
# awarded ``Points.value``
When we display a list of contestants along with their total_points value, it
results in an extra query for each result - i.e. the following queries are performed:
fetch list of contestants
fetch total_points value of 1st contestant
fetch total_points value of 2nd contestant
etc
I tried altering the queryset to prefetch the data as follows:
Contestant.objects.filter(...).prefetch_related('points')
..., however even though it works, the prefetched data is not utilized when
listing contestants (so each result still tries to fetch total_points
in a separate query).
Is it possible to:
somehow tell the ORM to use prefetched values for the #property field when
populating data for individual model objects (e.g. access the prefetched value inside the Contestant.total_points #property method)?
or to prefetch them in a different way (as opposed to the example above)?
or to use a completely different approach achieving the same result?
(I'm listing results in tastypie, if it matters.)
Thank you.
When your aim is to add aggregated values to each item, you should use annotate, instead of aggregate.
For example (a simple query, no additional methods required):
Contestant.objects.filter(...).annotate(total_points=Sum('points__value'))
If you really want to put this code out of your query: you can, but a model method is not a right way to do this. Methods on models are for operations on single instances. If you want to do something on a whole QuerySet use an ORM Manager instead.
With a Manager this would look like this:
class TotalPointsManager(models.Manager):
def get_queryset(self):
return super(TotalPointsManager, self).get_queryset().annotate(total_points=Sum('points__value'))
class Contestant(models.Model):
email = models.EmailField(max_length=255, unique=True)
objects = TotalPointsManager() # You are overriding the default manager!
and then you would construct your query as usual (you can drop prefetch_related):
Contestant.objects.filter(...)
...and total_points field would become "magically" available for every object.

get model instance based on its m2m relation to a list of objects

How to get model instances of Filter given they should be m2m related to a given list of Tags?
class Tag(models.Model):
name=models.CharField(max_length=20)
details=models.TextField(blank=True)
parenttag=models.ForeignKey('self',null=True,related_name="childtags")
class Filter(models.Model):
tags=models.ManyToManyField(Tag, related_name='infilters')
parenttag = models.ForeignKey(Tag, related_name = 'child_filters')
This must be a relatively simple question but I didnot get answer on google.
If you have a queryset of Tags and you want to get the Filter objects that relate to them you could use the in query lookup:
Filter.objects.filter(tags__id__in=tags.values_list('id'))