Django Rest framework, how to include '__all__' fields and a related field in ModelSerializer ?
I have two models, one with M2M relation and a related name. I want to include all fields in the serializer and the related field.
models.py:
class Pizza(models.Model):
name = models.CharField(max_length=50, unique=True)
toppings = models.ManyToManyField(Topping, null=True, blank=True, related_name='pizzas')
class Topping(models.Model):
name = models.CharField(max_length=50, unique=True)
price = models.IntegerField(default=0)
serializer.py:
class ToppingSerializer(serializers.ModelSerializer):
class Meta:
model = Topping
fields = '__all__'
This works but it doesn't include the related field.
fields = ['name', 'price', 'pizzas']
This works exactly as I want, but what happens when Toppings model has a lot of fields. I want to do something like :
fields = ['__all__', 'pizzas']
This syntax results in an error saying:
Field name
__all__
is not valid for model
Is there a way to achieve the wanted behavior? Or the fields must be typed manually when using a related name ?
Like @DanEEStart said, DjangoRestFramework don't have a simple way to extend the 'all' value for fields, because the get_field_names
methods seems to be designed to work that way.
But fortunately you can override this method to allow a simple way to include all fields and relations without enumerate a tons of fields.
I override this method like this:
class ToppingSerializer(serializers.ModelSerializer):
class Meta:
model = Topping
fields = '__all__'
extra_fields = ['pizzas']
def get_field_names(self, declared_fields, info):
expanded_fields = super(ToppingSerializer, self).get_field_names(declared_fields, info)
if getattr(self.Meta, 'extra_fields', None):
return expanded_fields + self.Meta.extra_fields
else:
return expanded_fields
Note that this method only change the behaviour of this serializer, and the extra_fields
attribute only works on this serializer class.
If you have a tons of serializer like this, you can create a intermediate class to include this get_fields_names
method in one place and reuse'em many times. Some like this:
class CustomSerializer(serializers.HyperlinkedModelSerializer):
def get_field_names(self, declared_fields, info):
expanded_fields = super(CustomSerializer, self).get_field_names(declared_fields, info)
if getattr(self.Meta, 'extra_fields', None):
return expanded_fields + self.Meta.extra_fields
else:
return expanded_fields
class ToppingSerializer(CustomSerializer):
class Meta:
model = Topping
fields = '__all__'
extra_fields = ['pizzas']
class AnotherSerializer(CustomSerializer):
class Meta:
model = Post
fields = '__all__'
extra_fields = ['comments']
I just checked the source code of Django Rest Framework. The behaviour you want seems not to be supported in the Framework.
The fields
option must be a list, a tuple or the text __all__
.
Here is a snippet of the relevant source code:
ALL_FIELDS = '__all__'
if fields and fields != ALL_FIELDS and not isinstance(fields, (list, tuple)):
raise TypeError(
'The `fields` option must be a list or tuple or "__all__". '
'Got %s.' % type(fields).__name__
)
You cannot add 'all' additionally to the tuple or list with fields...