I receive some data through Ajax that allows me to do some filtering on a model that has some m2m relations (say Model). I get a queryset, say “content” that I then need to send back using json. Here are simplified lines of code:
content = Models.objects.all() content = content.values return JsonResponse({"data":list(content)})
It was working fine but the project changed and I now need to include the values of some of the m2m related models attached to Model to each queryset result instances, problem is that content=Model.objects.all() will of course not give me these values without some looping so I am stuck. I remember using a depth option in DRF that would have done the job here but unfortunately I cannot do this now as the project is too advanced and live. Any way to add directly in the queryset the results of the m2m related values ? Many many thanks
Adding simplified models here :
class Content(models.Model): uuid = models.CharField(max_length=255, primary_key=True, default=uuid.uuid4, editable=False) pathology = models.ManyToManyField(Pathology, null=True, blank=True) organs = models.ManyToManyField(Organ, null=True, blank=True) title = models.CharField(verbose_name="Titre", max_length=255, null=True, blank=False) document = models.ForeignKey( 'wagtaildocs.Document', null=True, blank=True, on_delete=models.SET_NULL, related_name="+", ) description = RichTextField(null=True, blank=True) class Pathology(models.Model): title = models.CharField(max_length=255, null=True, blank=True) def __str__(self): return self.title class Organ(models.Model): title = models.CharField(max_length=255, null=True, blank=True) def __str__(self): return self.title
Advertisement
Answer
Please don’t use .values(…)
[Django-antipatterns], these erode the logic in the model layer. Furthere you can not use .values(…)
bidirectionally: you can only convert records into dictionaries, not deserialize dictionaries into model objects.
In PostgreSQL you can make use of ArrayAgg
[Django-doc], this is an aggregate that only works for PostgreSQL, and furthermore it is probably not ideal either, since again it makes adapting the serialization process cumbersome. You can use this as:
from django.contrib.postgres.aggregates import ArrayAgg content = Models.objects.values().annotate( organs=ArrayAgg('organs__pk') ) return JsonResponse({"data":list(content)})
Using The Django REST framework is however still possible with the current view. You can install this framework and define a serializer with:
from rest_framework import serializers class ContentSerializer(serializers.ModelSerializer): organs = serializers.PrimaryKeyRelatedField(many=True) class Meta: model = Content fields = '__all__'
Then in the view you can work with:
contents = Models.objects.prefetch_related('organs') serializer = ContentSerializer(contents, many=True) return JsonResponse({'data': serializer.data})