Custom Nested serializer and lookups - django-rest-framework

I have a Category model and a Menu Model and I want to display the JSON data as follows:
{
"category":{
"category": "Something",
"menu": [
{
"id": 1,
"dish": "Sample Dish",
"price": 150,
"restaurant": 1
},
{
"id": 1,
"dish": "Sample Dish",
"price": 150,
"restaurant": 1
},
}
}
Here are the models:
class Menu(models.Model):
dish = models.CharField(max_length=250)
category = models.ForeignKey('Menu_Category',on_delete=models.CASCADE,related_name='menu')
price = models.IntegerField()
restaurant = models.ForeignKey('Restaurant',on_delete=models.CASCADE,related_name='menu')
def __str__(self):
return self.dish
class Menu_Category(models.Model):
category = models.CharField(max_length=255,default='')
def __str__(self):
return self.category
class Meta:
verbose_name = 'Menu Category'
verbose_name_plural = 'Menu Categories'
and here is the seriailizer:
class MenuSerializer(serializers.Serializer):
class Meta:
model = Menu
fields =['dish','price','restaurant']
class MenuCategorySerializer(serializers.Serializer):
menu = MenuSerializer(read_only=True,many=True)
class Meta:
model = Menu_Category
fields = ['category','menu']
I have tried building up some custom nested serializers as well and cant seem to get the JSON data right.

You should be able to do this by creating the property on the category model that returns it's related menus. Just FYI(You can also use self.menu_set.all()). See below for the model change
class Menu_Category(models.Model):
category = models.CharField(max_length=255,default='')
def __str__(self):
return self.category
class Meta:
verbose_name = 'Menu Category'
verbose_name_plural = 'Menu Categories'
#property
def menu(self):
return Menu.objects.filter(category=self)
In the serializer, change the serializer inheritence to ModelSerializer. You shouldn't need to change anything else unless your property name in models.py is different to the serializer fieldname
Sources in Rest framework serializers
class MenuCategorySerializer(serializers.ModelSerializer):
menu = MenuSerializer(read_only=True,many=True, source='menu') # -- Source must be the same as declared in models.py property
class Meta:
model = Menu_Category
fields = ['category','menu']

You have to add id to MenuSerializer fields and you can use reverse relation menu_set with source parameter in serializer like below
class MenuSerializer(serializers.Serializer):
class Meta:
model = Menu
fields =['id', 'dish','price','restaurant']
class MenuCategorySerializer(serializers.Serializer):
menu = MenuSerializer(read_only=True,many=True, source='menu_set')
class Meta:
model = Menu_Category
fields = ['category','menu']

Related

Django rest framework depth serializer only in to_representation

I have a model with many foreing keys. I want to do a POST with only ids and recieve a response with depth 1. When I set depth=1 it´s doesn´t work.
class State(models.Model):
name = models.CharField(max_length=255)
class City(models.Model):
state = models.ForeingKey(State, on_delete=models.PROTECT)
name = models.CharField(max_length=255)
class CitySerializer(serializers.ModelSerializer):
class Meta:
model = City
fields = '__all__'
I wanna post some like this
{
"state":1,
"name":"City Name"
}
And get
{
"id":1,
"name":"City Name",
"state":{
"id":1,
"name": "State Name"
}
}
Did you try to make a StateSerializer and use it in CitySerializer?
class StateSerializer(serializers.ModelSerializer):
class Meta:
model = State
fields = '__all__'
class CitySerializer(serializers.ModelSerializer):
state = StateSerializer(read_only=True)
class Meta:
model = City
fields = '__all__'
Check the doc here.

Django Form. Overwrite model field

I've a Model
MODEL
from django.conf.global_settings import LANGUAGES
class Book(models.Model):
title = models.CharField(max_length=120)
language = models.CharField(max_length=56, choices=LANGUAGES, blank=True)
And Form
FORM
class SearchForm(forms.ModelForm):
class Meta:
model = Book
fields = ["title", "language"]
I use crispy to render the form and the problem is that I get label and required inherited from Book model. So I need to somehow inhertit the max_length form model fields and set new labels value.
MY APPROACH
class SearchForm(forms.ModelForm):
title = forms.CharField(
widget=forms.TextInput(attrs={"placeholder": "Title"}), required=False, label=""
)
language = forms.CharField(
widget=forms.TextInput(attrs={"placeholder": "Language"}), label=""
)
class Meta:
model = Book
fields = ["title","language"]
Now there are no labels and title is no required but there is no length limit on title and LANGUAGES choices inherited from Book model.
How to solve that?
Few seconds after post an idea came to my mind.
class Meta:
model = Book
fields = ["title", "language"]
labels = {
"title": "",
"language": ""
}
widgets = {
'title': forms.TextInput(attrs={'placeholder': 'Title'}),
'language': forms.Select(attrs={'option': 'Language'}),
}
Now the only question I've is how to set the first value for language field. At the moment is just -------------.

Atrribute from nested relations is not read in serializer

I'm now using DRF as a backend of my project.
i have product model like this
class product(models.Model):
product_name = models.CharField(max_length=160)
i have category model like this
class category(models.Model):
category_name = models.CharField(max_length=60)
category_icon = models.ImageField(upload_to='category)
because 1 product can have multiple category and a lot of image I create
class product_category(models.Model):
product = models.ForeignKey(product, on_delete=models.CASCADE, related_name='product_collections')
category = models.ForeignKey(category, on_delete=models.CASCADE, related_name='category_collections')
and the last model
class product_image(models.Model):
product = models.ForeignKey(product, on_delete=models.CASCADE,related_name='image_collections')
product_img = models.ImageField(upload_to='product')
Now I have Serializer like this
class ProductCategorySerializer(serializers.ModelSerializer):
category_name = serializers.CharField(source='category.category_name')
class Meta:
model = product_category
fields = ('product_id','category_id','category_name')
class ProductImageSerializer(serializers.ModelSerializer):
class Meta:
model = product_images
fields = ('product_img',)
class ProductSerializer(serializers.ModelSerializer):
category_collections = CategoryProductSerializers(many=True)
image_collections = ProductImageSerializer(many=True)
class Meta:
model = product
fields = ('id','product_name','image_collections','category_collections')
From that serializer DRF will return
Error like this
Got AttributeError when attempting to get a value for field category_collections on serializer ProductSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the product instance.
but if i remove that category_collections field like this
class ProductSerializer(serializers.ModelSerializer):
# category_collections = CategoryProductSerializers(many=True)
image_collections = ProductImageSerializer(many=True)
class Meta:
model = product
fields = ('id','product_name','image_collections')
Everything is going fine, whats wrong with that categories collection, is my eye not seeing the mistake ?

DRF How to parse text field and serialize result to set of models

I have next models in models.py
class Rule(models.Model):
name = models.CharField(max_length=30)
description = models.TextField()
class Question(models.Model):
question = models.TextField(default='')
answer = models.TextField(default='')
rules = models.TextField(default='') # here I decided to store rules names only, separated by coma
I have next serializers in serializers.py
class RuleSerializer(serializers.ModelSerializer):
class Meta:
model = Rule
fields = ['name', 'description']
class QuestionSerializer(serializers.ModelSerializer):
class Meta:
model = Question
fields = ['question', 'answer', 'rules']
as result output I have next JSON
[
{
"question": "How are you?",
"answer": "fine",
"rules": "rule 1, rule 2, rule 3"
}
]
but how can I convert rules string to objects? I found one solution, but it is means I should convert/serialize Rule manually
class RuleSerializer(serializers.ModelSerializer):
class Meta:
model = Rule
fields = ['name', 'description']
class QuestionSerializer(serializers.ModelSerializer):
rules = serializers.SerializerMethodField()
class Meta:
model = Question
fields = ['question', 'answer', 'rules']
def get_rules(self, obj):
names = [name.strip() for name in obj.rules.split(",")]
rules = Rule.objects.all().filter(name__in=names)
return [{
"name": rule.name,
"description": rule.description,
} for rule in rules]
And of course I can do it manually, but how to do it with already existing serializer?
def get_rules(self, obj):
names = [name.strip() for name in obj.rules.split(",")]
rules = Rule.objects.all().filter(name__in=names)
serializer = RuleSerializer(rules)
serializer.is_valid() # Mandatory
return serializer.data

Django Rest: Serializers Based on Multiple Querysets

I'm working with serializers as described by the link:Serializer relations (section PrimaryKeyRelatedField)
I have a slight different need, I'm sure it's really easy.
class Album(models.Model):
album_name = models.CharField(max_length=100)
artist = models.CharField(max_length=100)
class Track(models.Model):
album = models.ForeignKey(Album, related_name='tracks')
order = models.IntegerField()
title = models.CharField(max_length=100)
duration = models.IntegerField()
class SomeWidget(models.Model):
album = models.ForeignKey(Album)
track = models.ForeignKey(Track)
name = models.CharField(max_length=100)
description = models.CharField(max_length=100)
My need, I need to return the following:
{
'album_name': 'Things We Lost In The Fire',
'artist': 'Low',
'tracks': [
'1: Sunflower',
'2: Whitetail',
'3: Dinosaur Act',
...
],
'widget': [
{
'id': '1234',
'name': 'my widget',
'description': 'my description'
}
]
}
I am trying:
class WidgetField(serializers.RelatedField):
def to_representation(self, value):
return {
'id': '1234'
....
}
class TrackListingField(serializers.RelatedField):
def to_representation(self, value):
...
class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackListingField(many=True)
widget = WidgetField()
class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')
I keep getting the error:
AssertionError: Relational field must provide a `queryset` argument, override `get_queryset`, or set read_only=`True`.
Thanks
Ok, got it. It took a little bit of Googling / trial and error. Apparently you can override the function that returns data for the field.
For example:
class AlbumSerializer(serializers.ModelSerializer):
widget = serializers.SerializerMethodField()
def get_widget(self, data):
return {
'id': data.id
}
OR, you can do the following:
class WidgetSerializer(serializers.ModelSerializer):
class Meta:
model = Widget
fields = ('id', 'name', 'description',)
.... and in the AlbumSerializer.get_widget function:
def get_widget(self, data):
widget = Widget.objects.get(album=data.album, track=data.track)
return WidgetSerializer(widget, many=False, context=self.context).data
Finally, you don't have to use the function name "get_widget". You can name it whatever you want. Example:
class AlbumSerializer(serializers.ModelSerializer):
widget = serializers.SerializerMethodField("fn_override")
def fn_override(self, data):
....
You can follow the pattern in this SO question: Django REST Framework: adding additional field to ModelSerializer

Resources