Merge pull request #3345 from jpadilla/fields

Update ModelSerializer 'fields'/'exclude'/'__all__' behavior
This commit is contained in:
Tom Christie 2015-08-28 14:57:05 +01:00
commit f87573ffa3
3 changed files with 57 additions and 4 deletions

View File

@ -432,6 +432,7 @@ Declaring a `ModelSerializer` looks like this:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ('id', 'account_name', 'users', 'created')
By default, all the model fields on the class will be mapped to a corresponding serializer fields.
@ -453,7 +454,7 @@ To do so, open the Django shell, using `python manage.py shell`, then import the
## Specifying which fields to include
If you only want a subset of the default fields to be used in a model serializer, you can do so using `fields` or `exclude` options, just as you would with a `ModelForm`.
If you only want a subset of the default fields to be used in a model serializer, you can do so using `fields` or `exclude` options, just as you would with a `ModelForm`. It is strongly recommended that you explicitly set all fields that should be serialized using the `fields` attribute. This will make it less likely to result in unintentionally exposing data when your models change.
For example:
@ -462,7 +463,27 @@ For example:
model = Account
fields = ('id', 'account_name', 'users', 'created')
The names in the `fields` option will normally map to model fields on the model class.
You can also set the `fields` attribute to the special value `'__all__'` to indicate that all fields in the model should be used.
For example:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = '__all__'
You can set the `exclude` attribute of the to a list of fields to be excluded from the serializer.
For example:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
exclude = ('users',)
In the example above, if the `Account` model had 3 fields `account_name`, `users`, and `created`, this will result in the fields `account_name` and `created` to be serialized.
The names in the `fields` and `exclude` attributes will normally map to model fields on the model class.
Alternatively names in the `fields` options can map to properties or methods which take no arguments that exist on the model class.

View File

@ -12,6 +12,8 @@ response content is handled by parsers and renderers.
"""
from __future__ import unicode_literals
import warnings
from django.db import models
from django.db.models.fields import Field as DjangoModelField
from django.db.models.fields import FieldDoesNotExist
@ -51,6 +53,8 @@ LIST_SERIALIZER_KWARGS = (
'instance', 'data', 'partial', 'context', 'allow_null'
)
ALL_FIELDS = '__all__'
# BaseSerializer
# --------------
@ -943,13 +947,13 @@ class ModelSerializer(Serializer):
fields = getattr(self.Meta, 'fields', None)
exclude = getattr(self.Meta, 'exclude', None)
if fields and not isinstance(fields, (list, tuple)):
if fields and fields != ALL_FIELDS and not isinstance(fields, (list, tuple)):
raise TypeError(
'The `fields` option must be a list or tuple. Got %s.' %
type(fields).__name__
)
if exclude and not isinstance(exclude, (list, tuple)):
if exclude and exclude != ALL_FIELDS and not isinstance(exclude, (list, tuple)):
raise TypeError(
'The `exclude` option must be a list or tuple. Got %s.' %
type(exclude).__name__
@ -962,6 +966,19 @@ class ModelSerializer(Serializer):
)
)
if fields is None and exclude is None:
warnings.warn(
"Creating a ModelSerializer without either the 'fields' attribute "
"or the 'exclude' attribute will be prohibited. "
"The {serializer_class} serializer needs updating.".format(
serializer_class=self.__class__.__name__
),
PendingDeprecationWarning
)
if fields == ALL_FIELDS:
fields = None
if fields is not None:
# Ensure that all declared fields have also been included in the
# `Meta.fields` option.

View File

@ -321,6 +321,21 @@ class TestRegularFieldMappings(TestCase):
ExampleSerializer()
def test_fields_and_exclude_behavior(self):
class ImplicitFieldsSerializer(serializers.ModelSerializer):
class Meta:
model = RegularFieldsModel
class ExplicitFieldsSerializer(serializers.ModelSerializer):
class Meta:
model = RegularFieldsModel
fields = '__all__'
implicit = ImplicitFieldsSerializer()
explicit = ExplicitFieldsSerializer()
assert implicit.data == explicit.data
@pytest.mark.skipif(django.VERSION < (1, 8),
reason='DurationField is only available for django1.8+')