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.
Related
Suppose I have a VehicleSerializer
class VehicleSerializer(serializers.Serializer):
person = PersonSerializer()
class Meta:
model = Vehicle
fields = ('id', 'type', 'person')
I need to use this serializer for get as well as post api. For get request this should be same, but for post request, i need to send data like:
{
"type": "Car",
"person": 1 (the id of the person row)
}
How can i use same Vehicle Serializer to validate this request too? As the above serializer will take only the dict value for person key.
Any help will be appreciated.
I think you need to set the person_id field for writing.
class VehicleSerializer(serializers.ModelSerializer):
person = PersonSerializer(read_only = True)
person_id = serializers.IntegerField(write_only = True)
class Meta:
model = Vehicle
fields = ('id', 'type', 'person', 'person_id')
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
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']
# models.py
class Student(models.Model):
name = models.CharField( max_length=256)
student_numer = models.CharField(max_length=256)
teachers = models.ManyToManyField(Teacher)
class Teacher(models.Model):
name = models.CharField(max_length=256)
# serializers.py
class StudentSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = ('name', 'student_number', )
class TeacherSerializer(serializers.ModelSerializer):
students = StudentSerializer(many=True)
class Meta:
model = Teacher
fields = '__all__'
def update(self, instance, validated_data):
students = validated_data.pop('students')
# here I want delete all old students, and add all new students
# of course, I have more better way to update it, but here just for simple.
But in fact, if I update teacher instance, and if the students not change, it will
raise: student with this student_numer already exist.
I know why, because StudentSerializer's student_numer field has the validator check unique.
And I can add some code like this to fixed this problem:
class StudentSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = ('name', 'student_number', )
extra_kwargs = {
'student_numer': {
'validators':[]
}
}
Now I want to know is there any more better way ??
I'm trying to serialize a model containing a property field that I also want to serialize.
models.py:
class MyModel(models.Model):
name = models.CharField(max_length=100)
slug = models.AutoSlugField(populate_from='name')
#property
def ext_link(self):
return "/".join([settings.EXT_BASE_URL, self.slug])
serializers.py:
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ('name', 'ext_link')
When trying to get to the related URL, I'm getting a serializer exception (KeyError) on the ext_link property.
How can I serialize the ext_link property?
Because it's not a model field, it needs to be added explicitly to the serializer class
class MyModelSerializer(serializers.ModelSerializer):
ext_link = serializers.Field()
class Meta:
model = MyModel
fields = ('name', 'ext_link')
as #Robert Townley's comment, this work with version 3.8.2:
class MyModelSerializer(serializers.ModelSerializer):
ext_link = serializers.ReadOnlyField()
class Meta:
model = MyModel
fields = "__all__"
The accepted answer doesn't seem to work for me, nor does the ReadOnlyField.
However, I have had success when I use a field that corresponds to the return type of my property function.
So for the example, I would do this:
class MyModelSerializer(serializers.ModelSerializer):
ext_link = serializers.CharField()
class Meta:
model = MyModel
fields = ('name', 'ext_link')
I've been able to do this with ListField, DictField, and IntegerField as well.
Another thing you might want to do is add a property that its contents are not a string. Let's say you have a model called Person and another one called Food that look like this (we assume that each food is the favorite of only one person, making it a OneToMany connection):
class Person(models.Model):
name = models.CharField(max_length=255)
#property
def favorite_foods(self):
return Food.objects.filter(person=self.pk)
class Food(models.Model):
name = models.CharField(max_length=255)
persons_favorite = models.ForeignKey(Person, on_delete=models.CASCADE)
If you want to add favorite_foods in Person's serializer all you have to do is:
class PersonSerializer(serializers.ModelSerializer):
favorite_foods = FoodSerializer(read_only=True, many=True)
class Meta:
model = Person
fields = ('name', 'favorite_foods')