So I have two models here
class ProductType(models.Model):
product_type = models.CharField(max_length=50)
def __str__(self):
return self.product_type
class Product(models.Model):
product_name = models.CharField(max_length=50)
product_type = models.ForeignKey(ProductType, on_delete=models.CASCADE)
product_image = models.ImageField(blank=False, null=False, upload_to="products")
product_price = models.FloatField()
product_description = models.TextField(default="Product Description")
def __str__(self):
return self.product_name
And I have created an api using django-rest-framework to be consumed by the React frontend. However, when I try to get the product_type, it'd just give me a number instead of the name as shown below. So, how can I replace that when retrieving the data with Product.objects.all() in the view?
If you want to only show product_type instead of id you can use serializers.SerializerMethodField() in your ProductSerializer and with defining source in that method you can tell serializer to return your desired field.
Example:
class ProductSerializer(serializers.ModelSerializer):
product_type = serializers.SerializerMethodField(source='product_type__product_type')
class Meta:
model = Product
fields = ('id', 'product_name', 'product_image', 'product_price', 'product_description', 'product_type')
But if you want to serialize all the ProductType itself you can define a serializer for this model and then use this serializer in your ProductSerializer to serialize the whole object of ProductType.
Example:
class ProductTypeSerializer(serializers.ModelSerializer):
class Meta:
model = ProductType
fields = ('id', 'product_type',)
class ProductSerializer(serializers.ModelSerializer):
product_type = ProductTypeSerializer()
class Meta:
model = Product
fields = ('id', 'product_name', 'product_image', 'product_price', 'product_description', 'product_type')
The second solution is also known as nested serializers.
Related
I have this model that represents a bookmark or favorite. It has multiple foreign keys to other models. In the api I would like to pull in the data from each of the models that is referenced in the particular bookmark.
The model:
class Bookmark(models.Model):
marktype = models.CharField(max_length=10)
post = models.OneToOneField(Post, on_delete=models.CASCADE, null=True, blank=True)
question = models.OneToOneField(Question, on_delete=models.CASCADE, null=True, blank=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True, verbose_name="created at")
updated_at = models.DateTimeField(auto_now=True, verbose_name="updated at")
class Meta:
verbose_name = "bookmark"
verbose_name_plural = "bookmarks"
ordering = ["created_at"]
db_table = "bookmarks"
def __str__(self):
return "{}'s bookmark".format(self.owner.username)
I tried to use a SerializerMethodField but I get an error: 'NoneType' object has no attribute 'id'
Here is the serializer
class BookmarkSerializer(serializers.ModelSerializer):
post = serializers.SerializerMethodField()
question = serializers.SerializerMethodField()
class Meta:
model = Bookmark
fields = '__all__'
def get_post(self, obj):
obj = Post.objects.get(id=obj.post.id)
post = ShortPostSerializer(obj)
return post.data
def get_question(self, obj):
obj = Question.objects.get(id=obj.question.id)
question = ShortQuestionSerializer(obj)
return question.data
what am I doing wrong please?
You can update your serializer like the following (You can short it as you want or use your ShortQuestionSerializer as well instead of QuestionSerializer),
class QuestionSerializer(serializers.ModelSerializer):
class Meta:
model = Question
fields = '__all__'
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
class BookmarkSerializer(serializers.ModelSerializer):
post = PostSerializer()
question = QuestionSerializer()
class Meta:
model = Bookmark
fields = '__all__'
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'
models.py
class Product(models.Model):
product_name = models.CharField(max_length=32)
quantity = models.IntegerField()
remarks = models.TextField(blank=True)
class Customer(models.Model):
customer_name = models.CharField(max_length=50)
address = models.CharField(max_length=100)
bill_no = models.CharField(max_length=8)
product = models.ManyToManyField(Product)
class Sell(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
date = models.DateField(auto_now_add=True)
total = models.IntegerField()
vat = models.IntegerField()
serializers.py
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = '__all__'
class CustomerSerializer(serializers.ModelSerializer):
product = ProductSerializer(many=True, read_only=False)
class Meta:
model = Customer
fields = '__all__'
class SellSerializer(serializers.ModelSerializer):
class Meta:
model = Sell
fields = '__all__'
And after serializers and views I get that when i browse to input sell.
How do I automatically connect Customer object to Sell so that I dont need to select the customers objects? Using token or any idea?
Also how to override create() method on customer serializer to add products details from customer view?
If you need to pass customer to the sell serializer automaticaly. You can pass it to the serializer's save method in the view:
serializer.save(customer=request.user)
You need to exclude customer field from serializer:
class SellSerializer(serializers.ModelSerializer):
class Meta:
model = Sell
exclude = ('customer',)
To save nested product you can save all new products to the list and then pass this list to the product.add() method:
class CustomerSerializer(serializers.ModelSerializer):
product = ProductSerializer(many=True, read_only=False)
class Meta:
model = Customer
fields = '__all__'
def create(self, validated_data):
product_data = validated_data.pop('product')
customer = Custome.objects.create(**validated_data)
product_lits = []
for product_details in product_data:
product_list.append(Product.objects.create(**product_details))
customer.product.add(*product_list)
return customer
I have a django model named Event, which references Customer model.
event_name = models.CharField(max_length=200, unique=True)
customer = models.ForeignKey(customer_models.Customer, db_index=True,
on_delete=models.SET_NULL,
related_name='customer_events', null=True)
event_location = models.CharField(max_length=200, default='')
event_date = models.DateField()
I need to get the customer list along with the latest event name for each user in the API.
Customer serializers.py file is
class CustomerSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
fields = '__all__'
Customer views.py file is
class CustomerViewSet(viewsets.ModelViewSet):
queryset = Customer.objects.all()
serializer_class = CustomerSerializer
How can I accomplish this?
In your Customer model you can have a property that returns the latest event name for a Customer instance:
class Customer(models.Model):
...
#property
def latest_event_name(self):
"""Return latest event name."""
# self.customer_events.order_by('event_date').last()
latest_event = self.customer_events.order_by('-event_date').first()
return latest_event.event_name if latest_event else None
In your serializer you can then add a ReadOnlyField for latest_event_name:
class CustomerSerializer(serializers.ModelSerializer):
latest_event_name = serializers.ReadOnlyField()
class Meta:
model = Customer
fields = '__all__'