everyone.
What I want is get field of a related model by serializer.
I have 2 models:
class Question(models.Model):
question_text = models.CharField(max_length=200)
def __str__(self):
return self.question_text
class Test(models.Model):
test_name = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
question = models.ManyToManyField(Question, related_name='tests')
def __str__(self):
return self.test_name
Why I've used ManyToManyField?
Because I've followed here:
https://medium.com/django-rest/lets-build-a-basic-product-review-backend-with-drf-part-1-652dd9b95485
Now I can do smth like:
But I want get question_text in response.
What I tried:
class TestSerializer(serializers.HyperlinkedModelSerializer):
question_text = serializers.CharField(read_only=True, source="question.question_text")
class Meta:
model = Test
fields = ['pk', 'test_name', 'pub_date', 'question_text']
expandable_fields = {
'question': (QuestionSerializer, {'many': True})
}
But it returned:
I understand, that problem might be in DB relations, but I can't get it.
Thanks in advance!
Use nested serializer:
class QuestionSerializer(serializers.ModelSerializer):
class Meta:
model = Question
fields = ['question_text']
class TestSerializer(serializers.ModelSerializer):
question = QuestionSerializer(many=True)
class Meta:
model = Test
fields = ['pk', 'test_name', 'pub_date', 'question']
There are two ways to do this I don't know which one is better but this is them
1- create a separate Serializer for question and assgin related_name in models.py to him
class QuestionSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Test
fields = "__all__"
class TestSerializer(serializers.HyperlinkedModelSerializer):
tests = QuestionSerializer(many=True) ## related_name
class Meta:
model = Test
fields = ['pk', 'test_name', 'pub_date', 'tests']
2 - using serializers.SerializerMethodField()
class TestSerializer(serializers.HyperlinkedModelSerializer):
question = serializers.SerializerMethodField()
class Meta:
model = Test
fields = ['pk', 'test_name', 'pub_date', 'question']
def get_question(self, obj):
question = obj.tests.all()
serilaizer = QuestionSerializer(question, many=True)
return serilaizer.data
Related
I have two models
class Answer(models.Model):
answer = models.TextField()
created_at = models.DateTimeField(editable=False, default=timezone.now)
updated_at = models.DateTimeField(default=timezone.now)
user = models.ForeignKey('users.CustomUser', on_delete=models.PROTECT)
question = models.ForeignKey('Question', on_delete=models.PROTECT)
number_of_points = models.IntegerField(default=0)
moderate_status = models.BooleanField(default=False)
and
class Question(models.Model):
question_subject = models.TextField()
question_text = models.TextField(default=None, null=True, blank=True)
slug = models.SlugField(max_length=128, unique=True, null=False, editable=False)
created_at = models.DateTimeField(editable=False, default=timezone.now)
updated_at = models.DateTimeField(default=timezone.now)
user = models.ForeignKey('users.CustomUser', on_delete=models.PROTECT)
animal = models.ForeignKey('animals.Animal', on_delete=models.PROTECT)
serializers.py
class QuestionDetailSerializer(serializers.ModelSerializer):
answers = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Question
fields = '__all__'
views.py
class QuestionsDetailView(generics.ListAPIView):
queryset = Question.objects.all()
serializer_class = QuestionsSerializer
def get_queryset(self):
return super().get_queryset().filter(
id=self.kwargs['pk']
)
url.py
path('questions/<int:pk>', QuestionsDetailView.as_view()),
And i want to combine 2 queryset, one being already that filters the question by pk provided in the url, and the other queryset i'd like to give is Answer.objects.all().filter(question__id='pk'). Essentially i want to show all the questions with answers to a particular question.
I strongly suggest to use relationships anywhere you can.
Add related_name to Answer.question in models.py:
class Answer(models.Model):
...
question = models.ForeignKey('Question', on_delete=models.PROTECT, related_name='answers')
If id is enough in serializer, then in serializers.py:
class QuestionDetailSerializer(serializers.ModelSerializer):
# this is not needed anymore: answers = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Question
fields = ['question_subject ', 'question_text', ..., 'answers`]
But you can also create a function of model that you can include in similar way:
class Question(models.Model):
...
def get_answers(self):
return [answer.answer for answer in self.answers.all()] # it will return a list of TextFields of answers
serializers.py:
class QuestionDetailSerializer(serializers.ModelSerializer):
class Meta:
model = Question
fields = ['question_subject ', 'question_text', ..., 'get_answers`]
You won't exactly combine 2 queryset from different table. But you can use nested serializer.
class AnswerSerializer(serializers.ModelSerializer):
class Meta:
model = Anwser
fields = ['id', ...]
class QuestionDetailSerializer(serializers.ModelSerializer):
answer_set = AnswerSerializer(many=True, read_only=True)
class Meta:
model = Question
fields = ['id', ..., 'answer_set']
# OR
class QuestionDetailSerializer(serializers.ModelSerializer):
class Meta:
model = Question
fields = ['id']
def to_representation(self, instance):
data = super().to_representation(instance)
data['answers'] = AnswerSerializer(many=True, instance=instance.anwser_set.all()).data
return data
I have a User and Event model,
class User(AbstractUser):
bio = models.CharField(max_length=255, blank=False, default='')
class Event(models.Model):
name = models.CharField(max_length=50)
user = models.ManyToManyField("User", related_name='event_player')
I want to get the number of users registered in events, I tried the following ways:
Try 1
class EventSerializer(serializers.ModelSerializer):
players_count = serializers.IntegerField(
source='user_set.count',
read_only=True
)
class Meta:
model = Event
fields = ('id', 'name','players_count')
Try 2
class EventSerializer(serializers.ModelSerializer):
players_count = serializers.SerializerMethodField()
class Meta:
model = Event
fields = ('id', 'name','players_count')
def get_players_count(self, obj):
return obj.user_set.all().count()
Try 3
class EventSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
return {'id': instance.pk, 'players_count': instance.user__count}
class Meta:
model = Event
fields = ('id', 'name','players_count')
But none of them make a sense, how do I get the total count of nested ManyToMany Users. I'm new to DRF please help me to solve this.
Try 2 should work without _set
See Django ManyToMany Docs for more information
class EventSerializer(serializers.ModelSerializer):
players_count = serializers.SerializerMethodField()
class Meta:
model = Event
fields = ('id', 'name','players_count')
def get_players_count(self, obj):
return obj.user.all().count()
I have a pair of parent/children relation models like:
class Post(models.Model):
title = models.TextField(null=True)
content = models.TextField(null=True)
author = models.TextField(null=True)
created_time = models.DateTimeField(null=True)
class Comment(models.Model):
content = models.TextField(null=True)
created_time = models.DateTimeField(null=True)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
and the serializers are like:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = '__all__'
and finally views:
class PostView(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
class CommentView(viewsets.ModelViewSet):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
Now I want to created an API that returns a list of Posts, in which each Post will contain two additional fields, one be all_comments, and the other will be latest_comment. I understand this could be easily done in SQL using JOINs. I am new to Django. I wonder if there's any easy way to do it in Django. Thanks.
Hope this config works for you :)
class CommentPostSerializer(serializers.ModelSerializer): # New Serializer class
class Meta:
model = Comment
exclude = ('post',)
class PostSerializer(serializers.ModelSerializer):
all_comments = CommentPostSerializer(read_only=True, many=True, source='comment_set')
latest_comment = serializers.SerializerMethodField()
def get_latest_comment(self, post):
latest_comment = post.comment_set.last()
return CommentPostSerializer(latest_comment).data
class Meta:
model = Post
fields = '__all__'
When I make a call to my API (curl), I get the following:
{"id": 1, "name": "Sword", "persona": [1]}
I'm getting the 'id' of the persona, but not the properties within. How do I do this?
Models.py:
class Persona(models.Model):
name = models.CharField(max_length=255)
description = models.CharField(max_length=255)
icon = models.CharField(max_length=255,default='1')
def __str__(self):
return self.name
class Equipment(models.Model):
personas = models.ManyToManyField(Persona)
name = models.CharField(max_length=255)
def __str__(self):
return self.name
Serializers.py:
class EquipmentSerializer(serializers.ModelSerializer):
personas = serializers.PrimaryKeyRelatedField(queryset=Persona.objects.all(), many=True)
name = serializers.CharField(max_length=255)
class Meta:
model = Equipment
fields = ('id', 'name', 'persona')
You need two steps:
Create a Persona serializer class:
class PersonaSerializer(serializers.ModelSerializer):
class Meta:
model = Persona
Then you can "chain" this serializer to the Equipement one:
class EquipmentSerializer(serializers.ModelSerializer):
personas = PersonaSerializer(source='personas', many=True)
name = serializers.CharField(max_length=255)
class Meta:
model = Equipment
fields = ('id', 'name', 'personas')
For a deeper understanding of the above, have a look at this exemplary answer:
How do I include related model fields using Django Rest Framework?
Good luck :)
I have combined these two answers: one and two In the attempt to select only certain fields from nested objects without any success at all, the result is returning ALL fields from all tables.
serializers:
class NameTestTypeSerializer(serializers.ModelSerializer):
class Meta:
model = TestTypeModel
fields = 'name'
class ExecutedTestSerializer(serializers.ModelSerializer):
test_type = NameTestTypeSerializer
class Meta:
model = ExecutedTestModel
fields = ('id', 'result', 'test_type')
depth = 1
models:
class TestTypeModel(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(null=False, max_length=255, unique=True)
........
class Meta:
db_table = 'TestType'
class ExecutedTestModel(models.Model):
id = models.AutoField(primary_key=True)
test_type = models.ForeignKey(TestTypeModel, to_field='id')
result = models.IntegerField(null=False)
class Meta:
db_table = 'ExecutedTest'
viewset:
class ExecutedTestViewSet(viewsets.ModelViewSet):
permission_classes = (IsAuthenticatedOrReadOnly,)
serializer_class = ExecutedTestSerializer
def get_queryset(self):
queryset = ExecutedTestModel.objects.all().select_related('test_type').defer('test_type__executable' )
return queryset
How did you check that executable is fetched? In django you can access deferred fields, they are loaded from db on demand.
I believe the problem isn't in underscore notation, instead it is in the definition of the serializers.