PUT on ModelViewSet with nested object Django Rest Framework - python

I'm building an API with the django rest framework. I have these models:
class Organism(models.Model):
name = models.CharField(max_length=255)
address = models.ForeignKey(Address, on_delete=models.CASCADE)
type = models.ForeignKey(Type, on_delete=models.CASCADE)
class Address(models.Model):
street = models.CharField(max_length=255, blank=True)
class Type(models.Model):
name = models.CharField(max_length=255, blank=True)
This is the view for my mode Organism :
class OrganismViewSet(viewsets.ModelViewSet):
queryset = Organism.objects.all()
serializer_class = OrganismSerializer
pagination_class = StandardResultsSetPagination
filter_backends = (filters.SearchFilter, DjangoFilterBackend)
filter_class = OrganismFilter
search_fields = ('name')
And my serializer:
class OrganismSerializer(serializers.ModelSerializer):
addresse = AddressSerializer()
type = TypeSerializer()
class Meta:
model = Organism
fields = '__all__'
def update(self, instance, validated_data):
// What I should write to do something "elegant"
Let's imagine when I get my Organism, I have:
{
"address": {
"id": 1
"street": "test"
},
type: {
"id": 1,
"name": "type Organism"
},
"name":"TestTest",
}
So I'm trying to update an Organism (I want to change the name of the street but not create a new object AND change the Type which exists in my database) by sending this:
{
"address": {
"id": 1
"street": "new name"
},
type: {
"id": 2,
"name": "new type"
}
"name":"TestTest",
}
And the fact is I don't have the ID of my object in the parameter "validated_data" of the method update.
If you guys know how to proceed... Thank you in advance.

Related

Different representations of an object in the DRF [duplicate]

This question already has answers here:
DRF: Simple foreign key assignment with nested serializers?
(12 answers)
Closed 6 months ago.
I have a model:
class Schedule(LogSaveDeleteMixin, models.Model):
name = models.CharField(max_length=40)
start_date = models.DateTimeField(null=True, blank=True)
class DeliveryChannel(LogSaveDeleteMixin, models.Model):
name = models.CharField(unique=True, max_length=40)
state = models.CharField(choices=DeliveryChannelState.choices, default='draft', max_length=15)
schedule = models.ForeignKey(Schedule, null=True, on_delete=models.SET_NULL)
And standard ModelSerializer:
class DeliveryChannelsSerializer(serializers.ModelSerializer):
class Meta:
model = DeliveryChannel
fields = '__all__'
class ScheduleSerializer(serializers.ModelSerializer):
class Meta:
model = Schedule
fields = '__all__'
For read requests (GET) I want to receive schedule field as a nested serializer:
{
"id": 0,
"name": "string",
"state": "archived",
"schedule": {
"id": 0,
"name": "string",
"start_date": "2022-08-12T02:41:32.187Z",
}
}
But for writing (POST, PUT) I want to get only schedule id:
{
"id": 0,
"name": "string",
"state": "archived",
"schedule": 0
}
I would like to know the best practices for do this
You can just set some fields in the DeliveryChannelsSerializer.
class DeliveryChannelsSerializer(serializers.ModelSerializer):
schedule = ScheduleSerializer(read_only = True)
schedule_id = serializers.IntegerField(write_only = True)
class Meta:
model = DeliveryChannel
fields = '__all__'
extra_fields = ['schedule_id']
The POST payload should be like the following:
{
"name": "string",
"state": "archived",
"schedule_id": 1
}
You can make two different serializers for DeliveryChannels.
class ScheduleSerializer(serializers.ModelSerializer):
class Meta:
model = Schedule
fields = ['id', 'name', 'start_date']
class DeliveryChannelSerializer(serializers.ModelSerializer):
# Serializer for POST,PUT
class Meta:
model = DeliveryChannel
fields = ['id', 'name', 'state', 'schedule']
class DeliveryChannelDetailSerializer(serializers.ModelSerializer):
# Serializer for GET
schedule = ScheduleSerializer()
class Meta:
model = DeliveryChannel
fields = ['id', 'name', 'state', 'schedule']
if you use ModelViewSet you need to override get_serializer_clas
def get_serializer_class(self):
if self.action == "list":
return DeliveryChannelDetailSerializer
return DeliveryChannelSerializer

how to Serialize multiple objects in Django

I have 2 models for admin and member positions and I would like to get both of the models in one API call to fetch the data on my front end. How can I achieve this?
class ClinetAdminPosition(models.Model):
name = models.CharField(max_length=128, null=True)
company = models.ForeignKey(
to="Company", on_delete=models.CASCADE, related_name="admin_positions", null=True
)
modified_at = models.DateTimeField(verbose_name="Updated", auto_now=True, editable=True)
created_at = models.DateTimeField(verbose_name="Created", auto_now_add=True, editable=False)
def __str__(self):
return f"{self.name}"
class ClinetMangerPosition(models.Model):
name = models.CharField(max_length=128, null=True)
company = models.ForeignKey(
to="Company", on_delete=models.CASCADE, related_name="manger_positions", null=True
)
modified_at = models.DateTimeField(verbose_name="Updated", auto_now=True, editable=True)
created_at = models.DateTimeField(verbose_name="Created", auto_now_add=True, editable=False)
def __str__(self):
return f"{self.name}"
I want to get both models' data from 1 API request
to be like this:
[
{
"admin_positions": [
{
"name": "test",
"company": 1
},
{
"name": "test2",
"company": 1
},
{
"name": "test3",
"company": 1
}
],
"manger_position": [
{
"name": "test",
"company": 1
},
{
"name": "test2",
"company": 1
},
{
"name": "test3",
"company": 1
}
]
}
]
You can do something like this.
**This is your serializers classes **
class ClinetMangerPositionSerializer(ModelSerializer)
clas Meta:
model = ClinetMangerPosition
fields = ['name','company']
class ClinetAdminPositionSerializer(ModelSerializer):
class Meta:
model = ClinetAdminPosition
fields = ['name','company']
This will be your views class to display data.
from rest_framework.views import APIView
from . import serializers
from rest_framework.response import Response
class ViewName(APIView):
def get(self, request):
admin = ClinetAdminPosition.objects.all()
manager = ClinetMangerPosition.objects.all()
res :{
"admin_positions": serializers.ClinetAdminPositionSerializer(admin, many=True).data
"manager_positions":serializers.ClinetMangerPositionSerializer(manager, many-True).data
}
return Response(res)
something like this,
Let me know if you got any doubt
class ClinetMangerPositionSerializer(ModelSerializer)
clas Meta:
model = ClinetMangerPosition
fields = ['name','company']
class ClinetAdminPositionSerializer(ModelSerializer):
class Meta:
model = ClinetAdminPosition
fields = ['name','company']
class CompanySerializer(MOdelSerializer):
manger_position = ClinetAdminPositionSerializer(many=True,source
='clinetmangerposition')
admin_position = ClinetAdminPositionSerializer(many=True,source
='clinetadminposition')
class Meta:
model= Company
fields = ['manger_position','admin_position']

Correct way to get/create nested objects with DRF?

I have the following models:
class Platform(models.Model): # Get
key = models.CharField(max_length=32, unique=True)
class PlatformVersion(models.Model): # Get or create
platform = models.ForeignKey(Platform, on_delete=models.CASCADE)
major = models.IntegerField(db_index=True)
minor = models.IntegerField(db_index=True)
build = models.IntegerField(db_index=True)
class DeviceManufacturer(models.Model): # Get or create
name = models.CharField(max_length=64, unique=True)
class DeviceModel(models.Model): # Get or create
manufacturer = models.ForeignKey(DeviceManufacturer, on_delete=models.CASCADE)
platform = models.ForeignKey(Platform, on_delete=models.CASCADE)
# ...
# And many others
POST JSON looks like this:
{
"device_model": {
"manufacturer": {
"name": "samsung"
},
"platform": {
"key": "android"
}
},
"version": {
"platform": {
"key": "android"
},
"major": 8,
"minor": 1,
"build": 0
}
}
I have the following serializers:
class PlatformSerializer(serializers.ModelSerializer):
class Meta:
model = Platform
fields = ('name', 'key',)
extra_kwargs = {
'name': {
'read_only': True,
},
}
def create(self, validated_data):
return Platform.objects.get(key=validated_data['key'])
class PlatformVersionSerializer(serializers.ModelSerializer):
platform = PlatformSerializer()
class Meta:
model = PlatformVersion
fields = ('platform', 'major', 'minor', 'build',)
def create(self, validated_data):
# ?
pass
class DeviceManufacturerSerializer(serializers.ModelSerializer):
class Meta:
model = DeviceManufacturer
fields = ('name',)
def create(self, validated_data):
manufacturer, _ = DeviceManufacturer.objects.get_or_create(
defaults={
'name': validated_data['name'],
},
name=validated_data['name']
)
return manufacturer
class DeviceModelSerializer(serializers.ModelSerializer):
manufacturer = DeviceManufacturerSerializer()
platform = PlatformSerializer()
class Meta:
model = DeviceModel
fields = ('manufacturer', 'platform')
def create(self, validated_data):
# ?
pass
I've marked in serializers places where I don't know how to correctly do the object creation, as some of nested objects are «read-only» (I mean that I in fact use get, not create), some of them are get or create.
How should I do it?

Allow to post json with array of jsonobjects inside

I am a newbie in django world and I'm trying to create a simple rest service with those models:
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
class Album(models.Model):
artist = models.ForeignKey(Musician,on_delete=models.CASCADE)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
And those serializers:
class MusicianSerializer(serializers.ModelSerializer):
class Meta:
model = Musician
fields = ('id','first_name', 'last_name', 'instrument')
class AlbumSerializer(serializers.ModelSerializer):
class Meta:
model = Album
fields = ('id','artist', 'name', 'release_date', 'num_stars')
I want to be able to post a JSON like this one:
{
"first_name": "MusicNom",
"last_name": "MusicCognom",
"instrument": "Flauta",
"albums":
[
{
"name": "Album1",
"release_date": "2015-02-12",
"num_stars": 5
},
{
"id": 2,
"artist": 1,
"name": "AlbumNuevo",
"release_date": "2013-01-08",
"num_stars": 5
}
]
}
This JSON should create the musician and his albums. I've found some examples in the documentation that are useful using "GET" but I would like to do it with "POST".

Serializer is returning "Model object" instead of specific field

I have a problem. I'm creating an API with Django REST framework and I created serializer to return recipes and ingredients to cook it.
models.py:
class Recipes(models.Model):
title = models.CharField(max_length=50,null=True, blank=False, verbose_name=('name'))
class Tag(models.Model):
name = models.CharField(max_length=50,null=True, blank=False, verbose_name=('name'))
class ListTag(models.Model):
recipes = models.ForeignKey(Recipes, blank=False, on_delete=models.CASCADE, related_name='recipes')
tag = models.ForeignKey(Tag, blank=False, on_delete=models.CASCADE, related_name='tag')
I have the classes Recipes, Tag (ingredients) and ListTag is the list that contains each ingredient with the id of the receipt.
serializers.py
class RecipesSerializer(serializers.ModelSerializer):
ingredient = serializers.StringRelatedField(many=True, read_only=True, source='recipes')
class Meta:
model = Recipes
fields = ('title', 'ingredient')
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
class ListTagSerializer(serializers.ModelSerializer):
tag = serializers.SlugRelatedField(
read_only=True,
slug_field='name'
)
class Meta:
model = ListeTag
fields = ('recipes','tag')
Results
For RecipesSerializer
{
"title": "Pancakes à la canadienne",
"ingredient": [
"ListTag object",
"ListTag object",
"ListTag object"
]
}
But I want
{
"title": "Pancakes à la canadienne",
"ingredient": [
{
"id": 2,
"name": "milk"
},
{
"id": 3,
"name": "rice"
},
{
"id": 4,
"name": "salade"
},
{
"id": 5,
"name": "tomato"
}
]
}
or
{
"title": "Pancakes à la canadienne",
"ingredient": ["milk","rice","salade","tomato"]
}
You can do this by using Nested relationships like:
class RecipesSerializer(serializers.ModelSerializer):
recipes = ListTagSerializer(many=True,read_only=True)
class Meta:
model = Recipes
fields = ('title', 'recipes')
class ListTagSerializer(serializers.ModelSerializer):
id = serializers.ReadOnlyField(source='tag.id')
tag = serializers.SlugRelatedField(
read_only=True,
slug_field='name'
)
class Meta:
model = ListeTag
fields = ('id','tag')
Learn more about Nested relationships here

Categories