I am working just for my experience and I stacked with the following problem. There are three models: Book, Chapter, and Publisher. Each model is related to one another with the foreign key.
class Book(models.Model):
name = models.CharField(max_length=64)
publisher = models.ForeignKey('Publisher', on_delete=models.CASCADE, null=True, blank=True)
def __str__(self):
return self.name
class Chapter(models.Model):
name = models.CharField(max_length=128)
book = models.ForeignKey(Book, related_name='books', on_delete=models.CASCADE)
class Publisher(models.Model):
title = models.CharField(max_length=128)
description = models.TextField()
def __str__(self):
return self.title
class Meta:
ordering = ['title']
I want to serialize the data in PublisherDetailSerializer which should show their books and all chapters related to their books.
Here are my serializers:
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ('id', 'name', 'publisher')
def to_representation(self, instance):
ret = super().to_representation(instance)
return ret
class ChapterSerializer(serializers.ModelSerializer):
id = serializers.IntegerField()
name = serializers.CharField(required=False)
location = serializers.SerializerMethodField()
class Meta:
model = Chapter
fields = ('id', 'name', 'location')
def get_location(self, instance):
serializer = BookSerializer(instance.author, context=self.context)
return serializer.data
class PublisherSerializer(serializers.HyperlinkedModelSerializer):
books = BookSerializer(many=True, read_only=True)
class Meta:
model = Publisher
fields = ('id', 'title', 'description', 'books')
class PublisherDetailSerializer(PublisherSerializer):
chapters = serializers.SerializerMethodField()
class Meta:
model = Publisher
fields = ('id', 'title', 'description', 'books', 'chapters')
def get_chapters(self, instance):
serializer = ChapterSerializer(Chapter.objects.filter(location=instance.books.name), many=True, context=self.context)
return serializer.data
def to_representation(self, instance):
ret = super().to_representation(instance)
return ret
Here is my view for Publisher:
class PublisherViewSet(MultiSerializerViewSetMixin, viewsets.ReadOnlyModelViewSet):
queryset = Publisher.objects.all()
serializer_class = PublisherSerializer
serializer_action_classes = {
'list': PublisherSerializer,
'retrieve': PublisherDetailSerializer,
}
Showing publishers and their books shows the right information in the browsable API, but I don't know how to get chapters through books. How should I approach it?
Notes:
the related_name in a models.ForeignKey is not what you thing it is. it is the attribute name to be used on the otherside of the relationship, see the below code
you can just nest the serializers as shown below
Models
class Publisher(models.Model):
title = models.CharField(max_length=128)
description = models.TextField()
class Book(models.Model):
name = models.CharField(max_length=128)
publisher = models.ForeignKey(Publisher, related_name='books', on_delete=models.CASCADE)
class Chapter(models.Model):
name = models.CharField(max_length=128)
book = models.ForeignKey(Book, related_name='chapters', on_delete=models.CASCADE)
Serializers
class ChapterSerializer(serializers.ModelSerializer):
id = serializers.IntegerField()
name = serializers.CharField(required=False)
class Meta:
model = Chapter
fields = ('id', 'name')
class BookSerializer(serializers.ModelSerializer):
chapters = ChapterSerializer(many=True, read_only=True)
class Meta:
model = Book
fields = ('id', 'name', 'chapters')
class PublisherSerializer(serializers.HyperlinkedModelSerializer):
books = BookSerializer(many=True, read_only=True)
class Meta:
model = Publisher
fields = ('id', 'title', 'description', 'books')
Related
I have two models in my project: Author and Book. Each book has a foreignkey that points to the author of the book.
I want to write an api which retrieves and instance of an Author and shows the details of that specific person.
The problem is that I don't know how to include that said person's books in my API.
This is my models.py:
class Book(models.Model):
title = models.CharField(max_length=150)
rating = models.IntegerField(default=0, validators=[MaxValueValidator(10), MinValueValidator(0),])
summary = models.TextField()
author = models.ForeignKey(Author, null=True, on_delete=models.SET_NULL)
class Author(models.Model):
authorID = models.AutoField(primary_key=True)
name = models.CharField(max_length=200)
dateOfBirth = models.DateField(null=True)
nationality = models.CharField(null=True, max_length=255)
AND this is the method that didn't work for me:
# Serializers.py
class AuthorRetrieveSerializer(serializers.ModelSerializer):
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
bookDetails = BookSerializer(read_only=True, many=True)
class Meta:
model = Author
fields = ('name', 'dateOfBirth', 'nationality', 'bookDetails')
# Views.py
class AuthorRetrieveViewSet(RetrieveUpdateDestroyAPIView):
permission_classes = (AllowAny,)
serializer_class = serializers.AuthorRetrieveSerializer
queryset = Author.objects.all()
lookup_field = 'authorID'
def get_queryset(self):
return self.queryset
This code retrieves the Author details successfully but doesn't give me their Books.
Have you tried specifying the source on the serializer?
# Serializers.py
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
class AuthorRetrieveSerializer(serializers.ModelSerializer):
bookDetails = BookSerializer(read_only=True, many=True, source="book_set")#correction here
class Meta:
model = Author
fields = ('name', 'dateOfBirth', 'nationality', 'bookDetails')
I have two models which have one to many relationship and I want to serialize the related fields on both ends.
The models:
class Mechanic(models.Model):
name = models.CharField('Name', max_length=256)
status = models.CharField('Status', choices=constants.MECHANIC_STATUS, default=constants.MECHANIC_STATUS[0][0],
max_length=64)
current_lat = models.CharField('Current Latitude', max_length=64)
current_lng = models.CharField('Current Longitude', max_length=64)
def __str__(self):
return self.name
class Service(models.Model):
type = models.CharField('Service Type', choices=constants.SERVICE_TYPES,
default=constants.SERVICE_TYPES[0][0], max_length=64)
mechanic = models.ForeignKey(Mechanic, on_delete=models.CASCADE, related_name='services')
vehicle_type = models.CharField('Vehicle Type', choices=constants.VEHICLE_TYPES,
default=constants.VEHICLE_TYPES[0][0], max_length=64)
charges = models.IntegerField('Charges')
def __str__(self):
return "{}, {}".format(self.mechanic, self.type)
The serializers:
class ServiceSerializer(serializers.ModelSerializer):
mechanic = MechanicSerializer(read_only=True) # Throws an error
class Meta:
model = Service
fields = ('id', 'type', 'mechanic', 'vehicle_type', 'charges')
class MechanicSerializer(serializers.ModelSerializer):
services = ServiceSerializer(many=True, read_only=True)
class Meta:
model = Mechanic
fields = ('id', 'name', 'status', 'services', 'current_lat', 'current_lng')
read_only_fields = ('id',)
How do I approach this problem? I understand that I've created a cyclic dependency because both serializers depend on each other.
Is there a better way to do it?
as an extenstion to my comment in the OP, create a new serializer class of Mechanic model before the ServiceSerializer class
class MechanicShortSerializer(serializers.ModelSerializer):
class Meta:
model = Mechanic
fields = '__all__'
class ServiceSerializer(serializers.ModelSerializer):
mechanic = MechanicShortSerializer(read_only=True) # replace with new serializer
class Meta:
model = Service
fields = ('id', 'type', 'mechanic', 'vehicle_type', 'charges')
class MechanicSerializer(serializers.ModelSerializer):
services = ServiceSerializer(many=True, read_only=True)
class Meta:
model = Mechanic
fields = ('id', 'name', 'status', 'services', 'current_lat', 'current_lng')
read_only_fields = ('id',)
I have many-to-many relations and I want to serialize reverse relations.
Here are my models:
class Nutrition(models.Model):
name = models.CharField(max_length=30, blank=False)
def __str__(self):
return self.name
class Company(models.Model):
name = models.CharField(max_length=30, blank=False)
nutritions = models.ManyToManyField(Nutrition, blank=True, related_name="companyID")
def __str__(self):
return self.name
And here are my serializers:
class NutritionSerializer(serializers.ModelSerializer):
companyID = CompanySerializer(read_only=True, many=True)
class Meta:
model = Nutrition
fields=('id', 'name', 'companyID')
class CompanySerializer(serializers.ModelSerializer):
nutritions_list = NutritionSerializer(source="nutritions", read_only=True, many=True)
class Meta:
model = Company
fields = ('id', 'name', 'nutritions_list')
And I'm getting an error:
NameError: name 'CompanySerializer' is not defined
You are getting the name error because you call CompanySerializer:
class NutritionSerializer(serializers.ModelSerializer):
companyID = CompanySerializer(read_only=True, many=True)
...
before defining it (a couple of lines under):
class CompanySerializer(serializers.ModelSerializer):
...
What i would suggest doing is adding another serializer for Company which does not include NutritionSerializer so you can place it above both NutritionSerializer and CompanySerializer. Here is how it would look:
class SimpleCompanySerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = ('id', 'name')
class NutritionSerializer(serializers.ModelSerializer):
company = SimpleCompanySerializer(read_only=True, many=True)
class Meta:
model = Nutrition
fields=('id', 'name', 'company')
class CompanySerializer(serializers.ModelSerializer):
nutritions_list = NutritionSerializer(source="nutritions", read_only=True, many=True)
class Meta:
model = Company
fields = ('id', 'name', 'nutritions_list')
I have seen some related posts, but I am not sure what I need to do.
I have set up a view to serialize my test model which has nested models. I have set up the serializers, but I get the error "Got AttributeError when attempting to get a value for field Question on serializer TestSerializer.\nThe serializer field might be named incorrectly".
My Serializers:
class AnswerSerializer(serializers.ModelSerializer):
class Meta:
model = Answer
fields = ('id', 'number', 'text', 'iscorrect')
class QuestionSerializer(serializers.ModelSerializer):
answer = AnswerSerializer()
class Meta:
model = Question
fields = ('id', 'number', 'text', 'answer')
related_object = 'answer'
class TestSerializer(serializers.ModelSerializer):
question = QuestionSerializer()
class Meta:
model = Test
fields = ('id', 'name', 'question')
related_object = 'question'
My Models:
class Test(models.Model):
user = models.ForeignKey(User, on_delete=models.PROTECT)
name = models.CharField(max_length=255,default='',blank=False)
datecreated = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class Question(models.Model):
test = models.ForeignKey(Test, on_delete=models.CASCADE)
text = models.CharField(max_length=255,default='',blank=False)
number = models.IntegerField()
def __str__(self):
return self.text
class Answer(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
text = models.CharField(max_length=255,default='',blank=False)
number = models.IntegerField()
iscorrect = models.BooleanField(default=False)
def __str__(self):
return self.text
The call from the view:
serializer = TestSerializer(test, many=True)
You have set the related_name in the foreign key other wise default related name is {model_name}_set.
class Question(models.Model):
test = models.ForeignKey(Test, on_delete=models.CASCADE, related_name='questions')
text = models.CharField(max_length=255,default='',blank=False)
number = models.IntegerField()
def __str__(self):
return self.text
in serializer you can access that fields
class TestSerializer(serializers.ModelSerializer):
questions = QuestionSerializer(many=True)
class Meta:
model = Test
fields = ('id', 'name', 'question')
related_object = 'question'
I want to add json data from the listserializer to the DetailSerializer class. The serializer looks something like this:
serializer.py
class ListSerializer(serializers.ModelSerializer):
class Meta:
model = Fastest_laps
fields = '__all__'
class DetailSerializer(serializers.ModelSerializer):
listserializer = ListSerializer( read_only=True, many=True)
class Meta:
model = Driver
fields =
('place_of_birth','driver','listserializer','picture')
But i dont really see the data once i view it, i only see the detailserializer data( Driver model)
class Fastest_laps(models.Model):
driver_name = models.CharField(max_length=25, null=True)
grand_prix = models.CharField(max_length=15, blank=True)
car_model = models.CharField(max_length=50)
time_taken = models.CharField(blank=True, max_length=8)
def __str__(self):
return self.driver_name
class Driver(models.Model):
place_of_birth = models.CharField(max_length=25)
driver = models.ForeignKey(Fastest_laps,
db_column='driver_name')
picture = models.ImageField(blank=True, null=True)
def __str__(self):
return str(self.driver)
api.py
class FastLapsSet(ModelViewSet):
queryset = Fastest_laps.objects.all()
serializer_class = ListSerializer
class DriverSet(ModelViewSet):
queryset = Driver.objects.all()
serializer_class = DetailSerializer
you should rename your property as model name field:
class DetailSerializer(serializers.ModelSerializer):
driver = ListSerializer(read_only=True)
# ^^^
class Meta:
model = Driver
fields =
('place_of_birth','driver','driver','picture')
or add the source attribute:
class DetailSerializer(serializers.ModelSerializer):
listserializer = ListSerializer(source='driver', read_only=True)
#^^^^
class Meta:
model = Driver
fields =
('place_of_birth','driver','listserializer','picture')