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',)
Related
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')
In models:
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.IntegerField()
sub_category = models.ForeignKey(SubCategory, on_delete=models.SET_NULL, null=True, related_name='product')
class SubCategory(models.Model):
sub_category_name = models.CharField(max_length=50)
sub_category_img = models.ImageField(upload_to='sub_category_img')
In serializers:
class ProductSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Product
fields = ['url', 'id', 'name', 'price', 'sub_category']
class SubCategorySerializer(serializers.HyperlinkedModelSerializer):
product = ProductSerializer(many=True)
class Meta:
model = SubCategory
fields = ['url', 'id', 'sub_category_name', 'sub_category_img', 'product']
Using models, I can get access to Products.sub_category for a single queryset and also SubCategory.product reverse query using related_name in ForeignKey.
Using serializers, I can get access the ProductSerializer queryset using SubCategorySerializer but can't access SubCategorySerializer from ProductSerializer. I only get the url for sub_category when use ProductSerializer.
How can i reverse both in api endpoints?
I tried, in serializers,
class ProductSerializer(serializers.HyperlinkedModelSerializer):
sub = SubCategorySerializer()
class Meta:
model = Product
fields = ['url', 'id', 'name', 'price', 'sub_category', 'sub']
class SubCategorySerializer(serializers.HyperlinkedModelSerializer):
product = ProductSerializer(many=True)
class Meta:
model = SubCategory
fields = ['url', 'id', 'sub_category_name', 'sub_category_img', 'product']
It's not working. How can I get both queryset like models?
You can try like this:
class ProductSerializer(serializers.HyperlinkedModelSerializer):
sub_category = SubCategorySerializer()
class Meta:
model = Product
fields = ['url', 'id', 'name', 'price', 'sub_category']
Or just use depth=1 in Product Serializer:
class ProductSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Product
fields = ['url', 'id', 'name', 'price', 'sub_category']
depth = 1
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')
Trying to add 3rd nested serializer using django rest framework
how to add 3rd nested realation in given code -
models.py
class Category(models.Model):
cate_id = models.AutoField(primary_key=True)
cate_name = models.CharField(max_length=45)
class Meta:
managed = False
db_table = 'category'
class SubCategory(models.Model):
sub_cate_id = models.AutoField(primary_key=True)
sub_cate_name = models.CharField(max_length=45)
sub_fk = models.ForeignKey('Category', models.CASCADE, db_column='sub_fk')
class Meta:
managed = False
db_table = 'sub_category'
class Products(models.Model):
pro_id = models.AutoField(primary_key=True)
pro_name = models.CharField(max_length=45)
description = models.CharField(max_length=45, blank=True, null=True)
price = models.IntegerField()
quantity = models.IntegerField()
pro_cate_fk = models.ForeignKey('Category', models.CASCADE, db_column='pro_cate_fk')
pro_sub_fk = models.ForeignKey('SubCategory', models.CASCADE, db_column='pro_sub_fk')
image = models.CharField(max_length=205)
class Meta:
managed = False
db_table = 'products'
from rest_framework import serializers
from .models import Category,SubCategory,Products
class ProductsSerializer(serializers.ModelSerializer):
# x= ChildTable.objects.all().values
class Meta:
model = Products
fields = ('pro_id','pro_name','description','price','quantity','image')
class SubCategorySerializer(ProductsSerializer):
products_set = ProductsSerializer(many=True, read_only=True)
class Meta:
model = SubCategory
fields = ('sub_cate_name','sub_cate_id','products_set')
class CategorySerializer(SubCategorySerializer):
subcategory_set = ProductsSerializer(many=True, read_only=True,)
# pro_subcate_set = SubCategorySerializer(many=True, read_only=True)
class Meta:
model = Category
fields = ('cate_name','cate_id','subcategory_set')
Got this error while attempting -
Got AttributeError when attempting to get a value for field `pro_name` on serializer `ProductsSerializer`. The serializer field might be named incorrectly and not match any attribute or key on the `SubCategory` instance. Original exception text was: 'SubCategory' object has no attribute 'pro_name'.
Is it possible to connect 2 already connected serializer to another serializer?
Make a test and see if it works.
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('cate_id', 'cate_name')
class SubCategorySerializer(serializers.ModelSerializer):
class Meta:
model = SubCategory
fields = ('sub_cate_id', 'sub_cate_name', 'sub_fk')
def to_representation(self, instance):
response = super().to_represntation(instance)
response['sub_fk'] = CategorySerializer(instance.sub_fk).data
return response
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Products
fields = ('pro_id', 'pro_name', 'description', 'price', 'quantity', 'pro_cate_fk', 'pro_sub_fk', 'image')
def to_representation(self, instance):
response = super().to_representation(instance)
response['pro_cate_fk'] = CategorySerializer(instance.pro_cate_fk).data
response['pro_sub_fk'] = ProductSerializer(instance.pro_sub_fk).data
return response
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'