Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2a1485e

Browse files
Final bits of docs for ModelSerializer fields API
1 parent 48d15f6 commit 2a1485e

File tree

5 files changed

+132
-81
lines changed

5 files changed

+132
-81
lines changed

docs/api-guide/serializers.md

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ To do so, open the Django shell, using `python manage.py shell`, then import the
457457
name = CharField(allow_blank=True, max_length=100, required=False)
458458
owner = PrimaryKeyRelatedField(queryset=User.objects.all())
459459

460-
## Specifying which fields should be included
460+
## Specifying which fields to include
461461

462462
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`.
463463

@@ -499,7 +499,7 @@ You can add extra fields to a `ModelSerializer` or override the default fields b
499499

500500
Extra fields can correspond to any property or callable on the model.
501501

502-
## Specifying which fields should be read-only
502+
## Specifying read only fields
503503

504504
You may wish to specify multiple fields as read-only. Instead of adding each field explicitly with the `read_only=True` attribute, you may use the shortcut Meta option, `read_only_fields`.
505505

@@ -528,7 +528,7 @@ Please review the [Validators Documentation](/api-guide/validators/) for details
528528
---
529529

530530

531-
## Specifying additional keyword arguments for fields.
531+
## Additional keyword arguments
532532

533533
There is also a shortcut allowing you to specify arbitrary additional keyword arguments on fields, using the `extra_kwargs` option. Similarly to `read_only_fields` this means you do not need to explicitly declare the field on the serializer.
534534

@@ -567,31 +567,62 @@ The inner `Meta` class on serializers is not inherited from parent classes by de
567567

568568
Typically we would recommend *not* using inheritance on inner Meta classes, but instead declaring all options explicitly.
569569

570-
## Advanced `ModelSerializer` usage
570+
## Customizing field mappings
571571

572572
The ModelSerializer class also exposes an API that you can override in order to alter how serializer fields are automatically determined when instantiating the serializer.
573573

574-
#### `.serializer_field_mapping`
574+
Normally if a `ModelSerializer` does not generate the fields you need by default the you should either add them to the class explicitly, or simply use a regular `Serializer` class instead. However in some cases you may want to create a new base class that defines how the serializer fields are created for any given model.
575+
576+
### `.serializer_field_mapping`
575577

576578
A mapping of Django model classes to REST framework serializer classes. You can override this mapping to alter the default serializer classes that should be used for each model class.
577579

578-
#### `.serializer_relational_field`
580+
### `.serializer_relational_field`
579581

580582
This property should be the serializer field class, that is used for relational fields by default. For `ModelSerializer` this defaults to `PrimaryKeyRelatedField`. For `HyperlinkedModelSerializer` this defaults to `HyperlinkedRelatedField`.
581583

582-
#### The build field methods
584+
### The field_class and field_kwargs API
585+
586+
The following methods are called to determine the class and keyword arguments for each field that should be automatically included on the serializer. Each of these methods should return a two tuple of `(field_class, field_kwargs)`.
587+
588+
### `.build_standard_field(self, field_name, model_field)`
589+
590+
Called to generate a serializer field that maps to a standard model field.
591+
592+
The default implementation returns a serializer class based on the `serializer_field_mapping` attribute.
593+
594+
### `.build_relational_field(self, field_name, relation_info)`
595+
596+
Called to generate a serializer field that maps to a relational model field.
597+
598+
The default implementation returns a serializer class based on the `serializer_relational_field` attribute.
599+
600+
The `relation_info` argument is a named tuple, that contains `model_field`, `related_model`, `to_many` and `has_through_model` properties.
601+
602+
### `.build_nested_field(self, field_name, relation_info, nested_depth)`
603+
604+
Called to generate a serializer field that maps to a relational model field, when the `depth` option has been set.
605+
606+
The default implementation dynamically creates a nested serializer class based on either `ModelSerializer` or `HyperlinkedModelSerializer`.
607+
608+
The `nested_depth` will be the value of the `depth` option, minus one.
609+
610+
The `relation_info` argument is a named tuple, that contains `model_field`, `related_model`, `to_many` and `has_through_model` properties.
611+
612+
### `.build_property_field(self, field_name, model_class)`
583613

584-
#### `build_standard_field(**kwargs)`
614+
Called to generate a serializer field that maps to a property or zero-argument method on the model class.
585615

586-
#### `build_relational_field(**kwargs)`
616+
The default implementation returns a `ReadOnlyField` class.
587617

588-
#### `build_nested_field(**kwargs)`
618+
### `.build_url_field(self, field_name, model_class)`
589619

590-
#### `build_property_field(**kwargs)`
620+
Called to generate a serializer field for the serializer's own `url` field. The default implementation returns a `HyperlinkedIdentityField` class.
591621

592-
#### `build_url_field(**kwargs)`
622+
### `.build_unknown_field(self, field_name, model_class)`
593623

594-
#### `build_unknown_field(**kwargs)`
624+
Called when the field name did not map to any model field or model property.
625+
The default implementation raises an error, although subclasses may customize this behavior.
595626

596627
---
597628

docs_theme/css/default.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,10 @@ body a:hover{
239239
}
240240
}
241241

242+
h1 code, h2 code, h3 code, h4 code, h5 code {
243+
color: #333;
244+
}
245+
242246
/* sticky footer and footer */
243247
html, body {
244248
height: 100%;

rest_framework/serializers.py

Lines changed: 78 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -802,10 +802,25 @@ def get_fields(self):
802802
Return the dict of field names -> field instances that should be
803803
used for `self.fields` when instantiating the serializer.
804804
"""
805+
assert hasattr(self, 'Meta'), (
806+
'Class {serializer_class} missing "Meta" attribute'.format(
807+
serializer_class=self.__class__.__name__
808+
)
809+
)
810+
assert hasattr(self.Meta, 'model'), (
811+
'Class {serializer_class} missing "Meta.model" attribute'.format(
812+
serializer_class=self.__class__.__name__
813+
)
814+
)
815+
805816
declared_fields = copy.deepcopy(self._declared_fields)
806817
model = getattr(self.Meta, 'model')
807818
depth = getattr(self.Meta, 'depth', 0)
808819

820+
if depth is not None:
821+
assert depth >= 0, "'depth' may not be negative."
822+
assert depth <= 10, "'depth' may not be greater than 10."
823+
809824
# Retrieve metadata about fields & relationships on the model class.
810825
info = model_meta.get_field_info(model)
811826
field_names = self.get_field_names(declared_fields, info)
@@ -817,27 +832,32 @@ def get_fields(self):
817832
field_names, declared_fields, extra_kwargs
818833
)
819834

820-
# Now determine the fields that should be included on the serializer.
821-
ret = OrderedDict()
835+
# Determine the fields that should be included on the serializer.
836+
fields = OrderedDict()
837+
822838
for field_name in field_names:
839+
# If the field is explicitly declared on the class then use that.
823840
if field_name in declared_fields:
824-
# Field is explicitly declared on the class, use that.
825-
ret[field_name] = declared_fields[field_name]
841+
fields[field_name] = declared_fields[field_name]
826842
continue
827843

828844
# Determine the serializer field class and keyword arguments.
829-
field_cls, kwargs = self.build_field(field_name, info, model, depth)
845+
field_class, field_kwargs = self.build_field(
846+
field_name, info, model, depth
847+
)
830848

831-
# Populate any kwargs defined in `Meta.extra_kwargs`
832-
kwargs = self.build_field_kwargs(kwargs, extra_kwargs, field_name)
849+
# Include any kwargs defined in `Meta.extra_kwargs`
850+
field_kwargs = self.build_field_kwargs(
851+
field_kwargs, extra_kwargs, field_name
852+
)
833853

834854
# Create the serializer field.
835-
ret[field_name] = field_cls(**kwargs)
855+
fields[field_name] = field_class(**field_kwargs)
836856

837857
# Add in any hidden fields.
838-
ret.update(hidden_fields)
858+
fields.update(hidden_fields)
839859

840-
return ret
860+
return fields
841861

842862
# Methods for determining the set of field names to include...
843863

@@ -916,108 +936,105 @@ def get_default_field_names(self, declared_fields, model_info):
916936

917937
# Methods for constructing serializer fields...
918938

919-
def build_field(self, field_name, info, model, nested_depth):
939+
def build_field(self, field_name, info, model_class, nested_depth):
920940
"""
921941
Return a two tuple of (cls, kwargs) to build a serializer field with.
922942
"""
923943
if field_name in info.fields_and_pk:
924-
return self.build_standard_field(field_name, info, model)
944+
model_field = info.fields_and_pk[field_name]
945+
return self.build_standard_field(field_name, model_field)
925946

926947
elif field_name in info.relations:
948+
relation_info = info.relations[field_name]
927949
if not nested_depth:
928-
return self.build_relational_field(field_name, info, model)
950+
return self.build_relational_field(field_name, relation_info)
929951
else:
930-
return self.build_nested_field(field_name, info, model, nested_depth)
952+
return self.build_nested_field(field_name, relation_info, nested_depth)
931953

932-
elif hasattr(model, field_name):
933-
return self.build_property_field(field_name, info, model)
954+
elif hasattr(model_class, field_name):
955+
return self.build_property_field(field_name, model_class)
934956

935957
elif field_name == api_settings.URL_FIELD_NAME:
936-
return self.build_url_field(field_name, info, model)
958+
return self.build_url_field(field_name, model_class)
937959

938-
return self.build_unknown_field(field_name, info, model)
960+
return self.build_unknown_field(field_name, model_class)
939961

940-
def build_standard_field(self, field_name, info, model):
962+
def build_standard_field(self, field_name, model_field):
941963
"""
942964
Create regular model fields.
943965
"""
944966
field_mapping = ClassLookupDict(self.serializer_field_mapping)
945-
model_field = info.fields_and_pk[field_name]
946967

947-
field_cls = field_mapping[model_field]
948-
kwargs = get_field_kwargs(field_name, model_field)
968+
field_class = field_mapping[model_field]
969+
field_kwargs = get_field_kwargs(field_name, model_field)
949970

950-
if 'choices' in kwargs:
971+
if 'choices' in field_kwargs:
951972
# Fields with choices get coerced into `ChoiceField`
952973
# instead of using their regular typed field.
953-
field_cls = ChoiceField
954-
if not issubclass(field_cls, ModelField):
974+
field_class = ChoiceField
975+
if not issubclass(field_class, ModelField):
955976
# `model_field` is only valid for the fallback case of
956977
# `ModelField`, which is used when no other typed field
957978
# matched to the model field.
958-
kwargs.pop('model_field', None)
959-
if not issubclass(field_cls, CharField) and not issubclass(field_cls, ChoiceField):
979+
field_kwargs.pop('model_field', None)
980+
if not issubclass(field_class, CharField) and not issubclass(field_class, ChoiceField):
960981
# `allow_blank` is only valid for textual fields.
961-
kwargs.pop('allow_blank', None)
982+
field_kwargs.pop('allow_blank', None)
962983

963-
return field_cls, kwargs
984+
return field_class, field_kwargs
964985

965-
def build_relational_field(self, field_name, info, model):
986+
def build_relational_field(self, field_name, relation_info):
966987
"""
967988
Create fields for forward and reverse relationships.
968989
"""
969-
relation_info = info.relations[field_name]
970-
971-
field_cls = self.serializer_related_class
972-
kwargs = get_relation_kwargs(field_name, relation_info)
990+
field_class = self.serializer_related_class
991+
field_kwargs = get_relation_kwargs(field_name, relation_info)
973992

974993
# `view_name` is only valid for hyperlinked relationships.
975-
if not issubclass(field_cls, HyperlinkedRelatedField):
976-
kwargs.pop('view_name', None)
994+
if not issubclass(field_class, HyperlinkedRelatedField):
995+
field_kwargs.pop('view_name', None)
977996

978-
return field_cls, kwargs
997+
return field_class, field_kwargs
979998

980-
def build_nested_field(self, field_name, info, model, nested_depth):
999+
def build_nested_field(self, field_name, relation_info, nested_depth):
9811000
"""
9821001
Create nested fields for forward and reverse relationships.
9831002
"""
984-
relation_info = info.relations[field_name]
985-
9861003
class NestedSerializer(ModelSerializer):
9871004
class Meta:
988-
model = relation_info.related
989-
depth = nested_depth - 1
1005+
model = relation_info.related_model
1006+
depth = nested_depth
9901007

991-
field_cls = NestedSerializer
992-
kwargs = get_nested_relation_kwargs(relation_info)
1008+
field_class = NestedSerializer
1009+
field_kwargs = get_nested_relation_kwargs(relation_info)
9931010

994-
return field_cls, kwargs
1011+
return field_class, field_kwargs
9951012

996-
def build_property_field(self, field_name, info, model):
1013+
def build_property_field(self, field_name, model_class):
9971014
"""
9981015
Create a read only field for model methods and properties.
9991016
"""
1000-
field_cls = ReadOnlyField
1001-
kwargs = {}
1017+
field_class = ReadOnlyField
1018+
field_kwargs = {}
10021019

1003-
return field_cls, kwargs
1020+
return field_class, field_kwargs
10041021

1005-
def build_url_field(self, field_name, info, model):
1022+
def build_url_field(self, field_name, model_class):
10061023
"""
10071024
Create a field representing the object's own URL.
10081025
"""
1009-
field_cls = HyperlinkedIdentityField
1010-
kwargs = get_url_kwargs(model)
1026+
field_class = HyperlinkedIdentityField
1027+
field_kwargs = get_url_kwargs(model_class)
10111028

1012-
return field_cls, kwargs
1029+
return field_class, field_kwargs
10131030

1014-
def build_unknown_field(self, field_name, info, model):
1031+
def build_unknown_field(self, field_name, model_class):
10151032
"""
10161033
Raise an error on any unknown fields.
10171034
"""
10181035
raise ImproperlyConfigured(
10191036
'Field name `%s` is not valid for model `%s`.' %
1020-
(field_name, model.__class__.__name__)
1037+
(field_name, model_class.__name__)
10211038
)
10221039

10231040
def build_field_kwargs(self, kwargs, extra_kwargs, field_name):
@@ -1318,17 +1335,16 @@ def get_default_field_names(self, declared_fields, model_info):
13181335
list(model_info.forward_relations.keys())
13191336
)
13201337

1321-
def build_nested_field(self, field_name, info, model, nested_depth):
1338+
def build_nested_field(self, field_name, relation_info, nested_depth):
13221339
"""
13231340
Create nested fields for forward and reverse relationships.
13241341
"""
1325-
relation_info = info.relations[field_name]
1326-
13271342
class NestedSerializer(HyperlinkedModelSerializer):
13281343
class Meta:
1329-
model = relation_info.related
1344+
model = relation_info.related_model
13301345
depth = nested_depth - 1
13311346

1332-
field_cls = NestedSerializer
1333-
kwargs = get_nested_relation_kwargs(relation_info)
1334-
return field_cls, kwargs
1347+
field_class = NestedSerializer
1348+
field_kwargs = get_nested_relation_kwargs(relation_info)
1349+
1350+
return field_class, field_kwargs

rest_framework/utils/model_meta.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
RelationInfo = namedtuple('RelationInfo', [
2626
'model_field',
27-
'related',
27+
'related_model',
2828
'to_many',
2929
'has_through_model'
3030
])
@@ -77,7 +77,7 @@ def get_field_info(model):
7777
for field in [field for field in opts.fields if field.serialize and field.rel]:
7878
forward_relations[field.name] = RelationInfo(
7979
model_field=field,
80-
related=_resolve_model(field.rel.to),
80+
related_model=_resolve_model(field.rel.to),
8181
to_many=False,
8282
has_through_model=False
8383
)
@@ -86,7 +86,7 @@ def get_field_info(model):
8686
for field in [field for field in opts.many_to_many if field.serialize]:
8787
forward_relations[field.name] = RelationInfo(
8888
model_field=field,
89-
related=_resolve_model(field.rel.to),
89+
related_model=_resolve_model(field.rel.to),
9090
to_many=True,
9191
has_through_model=(
9292
not field.rel.through._meta.auto_created
@@ -99,7 +99,7 @@ def get_field_info(model):
9999
accessor_name = relation.get_accessor_name()
100100
reverse_relations[accessor_name] = RelationInfo(
101101
model_field=None,
102-
related=relation.model,
102+
related_model=relation.model,
103103
to_many=relation.field.rel.multiple,
104104
has_through_model=False
105105
)
@@ -109,7 +109,7 @@ def get_field_info(model):
109109
accessor_name = relation.get_accessor_name()
110110
reverse_relations[accessor_name] = RelationInfo(
111111
model_field=None,
112-
related=relation.model,
112+
related_model=relation.model,
113113
to_many=True,
114114
has_through_model=(
115115
(getattr(relation.field.rel, 'through', None) is not None)

0 commit comments

Comments
 (0)