10👍
IMHO, multiple serializers are only going to create more and more confusion.
Rather I would prefer below solution:
- Don’t change your viewset (leave it default)
- Add .validate() method in your serializer; along with other required .create or .update() etc. Here, real logic will go in
validate() method. Where based on request type we will be creating
validated_data dict as required by our serializer.
I think this is the cleanest approach.
See my similar problem and solution at DRF: Allow all fields in GET request but restrict POST to just one field
13👍
There is a feature of DRF where you can dynamically change the fields on the serializer http://www.django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields
My use case: use slug field on GET so we can see nice rep of a relation, but on POST/PUT switch back to the classic primary key update. Adjust your serializer to something like this:
class FooSerializer(serializers.ModelSerializer):
bar = serializers.SlugRelatedField(slug_field='baz', queryset=models.Bar.objects.all())
class Meta:
model = models.Foo
fields = '__all__'
def __init__(self, *args, **kwargs):
super(FooSerializer, self).__init__(*args, **kwargs)
try:
if self.context['request'].method in ['POST', 'PUT']:
self.fields['bar'] = serializers.PrimaryKeyRelatedField(queryset=models.Bar.objects.all())
except KeyError:
pass
The KeyError is sometimes thrown on code initialisation without a request, possibly unit tests.
Enjoy and use responsibly.
- [Django]-Django: how save bytes object to models.FileField?
- [Django]-Many-To-Many Fields View on Django Admin
- [Django]-Resource temporarily unavailable using uwsgi + nginx
5👍
You are looking for the get_serializer_class
method on the ViewSet
. This allows you to switch on request type for which serializer that you want to use.
from rest_framework import viewsets
class MyModelViewSet(viewsets.ModelViewSet):
model = MyModel
queryset = MyModel.objects.all()
def get_serializer_class(self):
if self.action in ('create', 'update', 'partial_update'):
return MySerializerWithPrimaryKeysForCreatingOrUpdating
else:
return MySerializerWithNestedData
- [Django]-Django admin TabularInline – is there a good way of adding a custom html column?
- [Django]-Django: Fat models and skinny controllers?
- [Django]-Do django db_index migrations run concurrently?
2👍
I know it’s a little late, but just in case someone else needs it. There are some third party packages for drf that allow dynamic setting of included serializer fields via the request query parameters (listed in the official docs: https://www.django-rest-framework.org/api-guide/serializers/#third-party-packages).
IMO the most complete ones are:
where (1) has more features than (2) (maybe too many, depending on what you want to do).
With (2) you can do things such as (extracted from the repo’s readme):
class CountrySerializer(FlexFieldsModelSerializer):
class Meta:
model = Country
fields = ['name', 'population']
class PersonSerializer(FlexFieldsModelSerializer):
country = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Person
fields = ['id', 'name', 'country', 'occupation']
expandable_fields = {
'country': (CountrySerializer, {'source': 'country', 'fields': ['name']})
}
The default response:
{
"id" : 13322,
"name" : "John Doe",
"country" : 12,
"occupation" : "Programmer"
}
When you do a GET /person/13322?expand=country, the response will change to:
{
"id" : 13322,
"name" : "John Doe",
"country" : {
"name" : "United States"
},
"occupation" : "Programmer",
}
Notice how population was ommitted from the nested country object. This is because fields was set to [‘name’] when passed to the embedded CountrySerializer.
This way you can keep your POST requests including just an id, and “expand” GET responses to include more details.
- [Django]-How do I reuse HTML snippets in a django view
- [Django]-Django QuerySet order
- [Django]-UUID as default value in Django model
0👍
The way I ended up dealing with this problem was having another serializer for when it’s a related field.
class HumanSerializer(PersonSerializer):
class Meta:
model = Human
fields = PersonSerializer.Meta.fields + (
'firstname',
'middlename',
'lastname',
'sex',
'date_of_birth',
'balance'
)
read_only_fields = ('name',)
class HumanRelatedSerializer(HumanSerializer):
def to_internal_value(self, data):
return self.Meta.model.objects.get(id=data['id'])
class PhoneNumberSerializer(serializers.ModelSerializer):
contact = HumanRelatedSerializer()
class Meta:
model = PhoneNumber
fields = (
'id',
'contact',
'phone',
'extension'
)
You could do something like this, but for the RelatedSerializer do:
def to_internal_value(self, data):
return self.Meta.model.objects.get(id=data)
Thus, when serializing, you serialize the related object, and when de-serializing, you only need the id to get the related object.
- [Django]-Django Footer and header on each page with {% extends }
- [Django]-Override existing Django Template Tags
- [Django]-Django Installed Apps Location