mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-01-23 15:54:16 +03:00
Drop label from serializer fields when not needed
This commit is contained in:
parent
afb28a44ad
commit
40dc588a37
|
@ -80,6 +80,10 @@ def set_value(dictionary, keys, value):
|
||||||
dictionary[keys[-1]] = value
|
dictionary[keys[-1]] = value
|
||||||
|
|
||||||
|
|
||||||
|
def field_name_to_label(field_name):
|
||||||
|
return field_name.replace('_', ' ').capitalize()
|
||||||
|
|
||||||
|
|
||||||
class SkipField(Exception):
|
class SkipField(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -158,7 +162,7 @@ class Field(object):
|
||||||
|
|
||||||
# `self.label` should deafult to being based on the field name.
|
# `self.label` should deafult to being based on the field name.
|
||||||
if self.label is None:
|
if self.label is None:
|
||||||
self.label = self.field_name.replace('_', ' ').capitalize()
|
self.label = field_name_to_label(self.field_name)
|
||||||
|
|
||||||
# self.source should default to being the same as the field name.
|
# self.source should default to being the same as the field name.
|
||||||
if self.source is None:
|
if self.source is None:
|
||||||
|
|
|
@ -15,11 +15,12 @@ from django.core.exceptions import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
|
from django.utils.text import capfirst
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from rest_framework.compat import clean_manytomany_helptext
|
from rest_framework.compat import clean_manytomany_helptext
|
||||||
from rest_framework.fields import empty, set_value, Field, SkipField
|
from rest_framework.fields import empty, set_value, Field, SkipField
|
||||||
from rest_framework.settings import api_settings
|
from rest_framework.settings import api_settings
|
||||||
from rest_framework.utils import html, modelinfo, representation
|
from rest_framework.utils import html, model_meta, representation
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
# Note: We do the following so that users of the framework can use this style:
|
# Note: We do the following so that users of the framework can use this style:
|
||||||
|
@ -334,6 +335,14 @@ def lookup_class(mapping, instance):
|
||||||
raise KeyError('Class %s not found in lookup.', cls.__name__)
|
raise KeyError('Class %s not found in lookup.', cls.__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def needs_label(model_field, field_name):
|
||||||
|
"""
|
||||||
|
Returns `True` if the label based on the model's verbose name
|
||||||
|
is not equal to the default label it would have based on it's field name.
|
||||||
|
"""
|
||||||
|
return capfirst(model_field.verbose_name) != field_name_to_label(field_name)
|
||||||
|
|
||||||
|
|
||||||
class ModelSerializer(Serializer):
|
class ModelSerializer(Serializer):
|
||||||
field_mapping = {
|
field_mapping = {
|
||||||
models.AutoField: IntegerField,
|
models.AutoField: IntegerField,
|
||||||
|
@ -397,54 +406,55 @@ class ModelSerializer(Serializer):
|
||||||
"""
|
"""
|
||||||
Return all the fields that should be serialized for the model.
|
Return all the fields that should be serialized for the model.
|
||||||
"""
|
"""
|
||||||
info = modelinfo.get_field_info(self.opts.model)
|
info = model_meta.get_field_info(self.opts.model)
|
||||||
ret = SortedDict()
|
ret = SortedDict()
|
||||||
|
|
||||||
serializer_url_field = self.get_url_field()
|
serializer_url_field = self.get_url_field()
|
||||||
if serializer_url_field:
|
if serializer_url_field:
|
||||||
ret[api_settings.URL_FIELD_NAME] = serializer_url_field
|
ret[api_settings.URL_FIELD_NAME] = serializer_url_field
|
||||||
|
|
||||||
serializer_pk_field = self.get_pk_field(info.pk)
|
field_name = info.pk.name
|
||||||
|
serializer_pk_field = self.get_pk_field(field_name, info.pk)
|
||||||
if serializer_pk_field:
|
if serializer_pk_field:
|
||||||
ret[info.pk.name] = serializer_pk_field
|
ret[field_name] = serializer_pk_field
|
||||||
|
|
||||||
# Regular fields
|
# Regular fields
|
||||||
for field_name, field in info.fields.items():
|
for field_name, field in info.fields.items():
|
||||||
ret[field_name] = self.get_field(field)
|
ret[field_name] = self.get_field(field_name, field)
|
||||||
|
|
||||||
# Forward relations
|
# Forward relations
|
||||||
for field_name, relation_info in info.forward_relations.items():
|
for field_name, relation_info in info.forward_relations.items():
|
||||||
if self.opts.depth:
|
if self.opts.depth:
|
||||||
ret[field_name] = self.get_nested_field(*relation_info)
|
ret[field_name] = self.get_nested_field(field_name, *relation_info)
|
||||||
else:
|
else:
|
||||||
ret[field_name] = self.get_related_field(*relation_info)
|
ret[field_name] = self.get_related_field(field_name, *relation_info)
|
||||||
|
|
||||||
# Reverse relations
|
# Reverse relations
|
||||||
for accessor_name, relation_info in info.reverse_relations.items():
|
for accessor_name, relation_info in info.reverse_relations.items():
|
||||||
if accessor_name in self.opts.fields:
|
if accessor_name in self.opts.fields:
|
||||||
if self.opts.depth:
|
if self.opts.depth:
|
||||||
ret[accessor_name] = self.get_nested_field(*relation_info)
|
ret[accessor_name] = self.get_nested_field(accessor_name, *relation_info)
|
||||||
else:
|
else:
|
||||||
ret[accessor_name] = self.get_related_field(*relation_info)
|
ret[accessor_name] = self.get_related_field(accessor_name, *relation_info)
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def get_url_field(self):
|
def get_url_field(self):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_pk_field(self, model_field):
|
def get_pk_field(self, field_name, model_field):
|
||||||
"""
|
"""
|
||||||
Returns a default instance of the pk field.
|
Returns a default instance of the pk field.
|
||||||
"""
|
"""
|
||||||
return self.get_field(model_field)
|
return self.get_field(field_name, model_field)
|
||||||
|
|
||||||
def get_nested_field(self, model_field, related_model, to_many, has_through_model):
|
def get_nested_field(self, field_name, model_field, related_model, to_many, has_through_model):
|
||||||
"""
|
"""
|
||||||
Creates a default instance of a nested relational field.
|
Creates a default instance of a nested relational field.
|
||||||
|
|
||||||
Note that model_field will be `None` for reverse relationships.
|
Note that model_field will be `None` for reverse relationships.
|
||||||
"""
|
"""
|
||||||
class NestedModelSerializer(ModelSerializer): # Not right!
|
class NestedModelSerializer(ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = related_model
|
model = related_model
|
||||||
depth = self.opts.depth - 1
|
depth = self.opts.depth - 1
|
||||||
|
@ -454,7 +464,7 @@ class ModelSerializer(Serializer):
|
||||||
kwargs['many'] = True
|
kwargs['many'] = True
|
||||||
return NestedModelSerializer(**kwargs)
|
return NestedModelSerializer(**kwargs)
|
||||||
|
|
||||||
def get_related_field(self, model_field, related_model, to_many, has_through_model):
|
def get_related_field(self, field_name, model_field, related_model, to_many, has_through_model):
|
||||||
"""
|
"""
|
||||||
Creates a default instance of a flat relational field.
|
Creates a default instance of a flat relational field.
|
||||||
|
|
||||||
|
@ -474,8 +484,8 @@ class ModelSerializer(Serializer):
|
||||||
if model_field:
|
if model_field:
|
||||||
if model_field.null or model_field.blank:
|
if model_field.null or model_field.blank:
|
||||||
kwargs['required'] = False
|
kwargs['required'] = False
|
||||||
if model_field.verbose_name:
|
if model_field.verbose_name and needs_label(model_field, field_name):
|
||||||
kwargs['label'] = model_field.verbose_name
|
kwargs['label'] = capfirst(model_field.verbose_name)
|
||||||
if not model_field.editable:
|
if not model_field.editable:
|
||||||
kwargs['read_only'] = True
|
kwargs['read_only'] = True
|
||||||
kwargs.pop('queryset', None)
|
kwargs.pop('queryset', None)
|
||||||
|
@ -485,7 +495,7 @@ class ModelSerializer(Serializer):
|
||||||
|
|
||||||
return PrimaryKeyRelatedField(**kwargs)
|
return PrimaryKeyRelatedField(**kwargs)
|
||||||
|
|
||||||
def get_field(self, model_field):
|
def get_field(self, field_name, model_field):
|
||||||
"""
|
"""
|
||||||
Creates a default instance of a basic non-relational field.
|
Creates a default instance of a basic non-relational field.
|
||||||
"""
|
"""
|
||||||
|
@ -496,8 +506,8 @@ class ModelSerializer(Serializer):
|
||||||
if model_field.null or model_field.blank:
|
if model_field.null or model_field.blank:
|
||||||
kwargs['required'] = False
|
kwargs['required'] = False
|
||||||
|
|
||||||
if model_field.verbose_name is not None:
|
if model_field.verbose_name and needs_label(model_field, field_name):
|
||||||
kwargs['label'] = model_field.verbose_name
|
kwargs['label'] = capfirst(model_field.verbose_name)
|
||||||
|
|
||||||
if model_field.help_text:
|
if model_field.help_text:
|
||||||
kwargs['help_text'] = model_field.help_text
|
kwargs['help_text'] = model_field.help_text
|
||||||
|
@ -642,11 +652,11 @@ class HyperlinkedModelSerializer(ModelSerializer):
|
||||||
|
|
||||||
return HyperlinkedIdentityField(**kwargs)
|
return HyperlinkedIdentityField(**kwargs)
|
||||||
|
|
||||||
def get_pk_field(self, model_field):
|
def get_pk_field(self, field_name, model_field):
|
||||||
if self.opts.fields and model_field.name in self.opts.fields:
|
if self.opts.fields and model_field.name in self.opts.fields:
|
||||||
return self.get_field(model_field)
|
return self.get_field(model_field)
|
||||||
|
|
||||||
def get_related_field(self, model_field, related_model, to_many, has_through_model):
|
def get_related_field(self, field_name, model_field, related_model, to_many, has_through_model):
|
||||||
"""
|
"""
|
||||||
Creates a default instance of a flat relational field.
|
Creates a default instance of a flat relational field.
|
||||||
"""
|
"""
|
||||||
|
@ -665,8 +675,8 @@ class HyperlinkedModelSerializer(ModelSerializer):
|
||||||
if model_field:
|
if model_field:
|
||||||
if model_field.null or model_field.blank:
|
if model_field.null or model_field.blank:
|
||||||
kwargs['required'] = False
|
kwargs['required'] = False
|
||||||
if model_field.verbose_name:
|
if model_field.verbose_name and needs_label(model_field, field_name):
|
||||||
kwargs['label'] = model_field.verbose_name
|
kwargs['label'] = capfirst(model_field.verbose_name)
|
||||||
if not model_field.editable:
|
if not model_field.editable:
|
||||||
kwargs['read_only'] = True
|
kwargs['read_only'] = True
|
||||||
kwargs.pop('queryset', None)
|
kwargs.pop('queryset', None)
|
||||||
|
|
|
@ -36,25 +36,25 @@ class RegularFieldsModel(models.Model):
|
||||||
|
|
||||||
REGULAR_FIELDS_REPR = """
|
REGULAR_FIELDS_REPR = """
|
||||||
TestSerializer():
|
TestSerializer():
|
||||||
auto_field = IntegerField(label='auto field', read_only=True)
|
auto_field = IntegerField(read_only=True)
|
||||||
big_integer_field = IntegerField(label='big integer field')
|
big_integer_field = IntegerField()
|
||||||
boolean_field = BooleanField(default=False, label='boolean field')
|
boolean_field = BooleanField(default=False)
|
||||||
char_field = CharField(label='char field', max_length=100)
|
char_field = CharField(max_length=100)
|
||||||
comma_seperated_integer_field = CharField(label='comma seperated integer field', max_length=100, validators=[<django.core.validators.RegexValidator object>])
|
comma_seperated_integer_field = CharField(max_length=100, validators=[<django.core.validators.RegexValidator object>])
|
||||||
date_field = DateField(label='date field')
|
date_field = DateField()
|
||||||
datetime_field = DateTimeField(label='datetime field')
|
datetime_field = DateTimeField()
|
||||||
decimal_field = DecimalField(decimal_places=1, label='decimal field', max_digits=3)
|
decimal_field = DecimalField(decimal_places=1, max_digits=3)
|
||||||
email_field = EmailField(label='email field', max_length=100)
|
email_field = EmailField(max_length=100)
|
||||||
float_field = FloatField(label='float field')
|
float_field = FloatField()
|
||||||
integer_field = IntegerField(label='integer field')
|
integer_field = IntegerField()
|
||||||
null_boolean_field = BooleanField(label='null boolean field', required=False)
|
null_boolean_field = BooleanField(required=False)
|
||||||
positive_integer_field = IntegerField(label='positive integer field')
|
positive_integer_field = IntegerField()
|
||||||
positive_small_integer_field = IntegerField(label='positive small integer field')
|
positive_small_integer_field = IntegerField()
|
||||||
slug_field = SlugField(label='slug field', max_length=100)
|
slug_field = SlugField(max_length=100)
|
||||||
small_integer_field = IntegerField(label='small integer field')
|
small_integer_field = IntegerField()
|
||||||
text_field = CharField(label='text field')
|
text_field = CharField()
|
||||||
time_field = TimeField(label='time field')
|
time_field = TimeField()
|
||||||
url_field = URLField(label='url field', max_length=100)
|
url_field = URLField(max_length=100)
|
||||||
""".strip()
|
""".strip()
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,9 +81,9 @@ class RelationalModel(models.Model):
|
||||||
RELATIONAL_FLAT_REPR = """
|
RELATIONAL_FLAT_REPR = """
|
||||||
TestSerializer():
|
TestSerializer():
|
||||||
id = IntegerField(label='ID', read_only=True)
|
id = IntegerField(label='ID', read_only=True)
|
||||||
foreign_key = PrimaryKeyRelatedField(label='foreign key', queryset=ForeignKeyTargetModel.objects.all())
|
foreign_key = PrimaryKeyRelatedField(queryset=ForeignKeyTargetModel.objects.all())
|
||||||
one_to_one = PrimaryKeyRelatedField(label='one to one', queryset=OneToOneTargetModel.objects.all())
|
one_to_one = PrimaryKeyRelatedField(queryset=OneToOneTargetModel.objects.all())
|
||||||
many_to_many = PrimaryKeyRelatedField(label='many to many', many=True, queryset=ManyToManyTargetModel.objects.all())
|
many_to_many = PrimaryKeyRelatedField(many=True, queryset=ManyToManyTargetModel.objects.all())
|
||||||
""".strip()
|
""".strip()
|
||||||
|
|
||||||
|
|
||||||
|
@ -92,22 +92,22 @@ TestSerializer():
|
||||||
id = IntegerField(label='ID', read_only=True)
|
id = IntegerField(label='ID', read_only=True)
|
||||||
foreign_key = NestedModelSerializer(read_only=True):
|
foreign_key = NestedModelSerializer(read_only=True):
|
||||||
id = IntegerField(label='ID', read_only=True)
|
id = IntegerField(label='ID', read_only=True)
|
||||||
name = CharField(label='name', max_length=100)
|
name = CharField(max_length=100)
|
||||||
one_to_one = NestedModelSerializer(read_only=True):
|
one_to_one = NestedModelSerializer(read_only=True):
|
||||||
id = IntegerField(label='ID', read_only=True)
|
id = IntegerField(label='ID', read_only=True)
|
||||||
name = CharField(label='name', max_length=100)
|
name = CharField(max_length=100)
|
||||||
many_to_many = NestedModelSerializer(many=True, read_only=True):
|
many_to_many = NestedModelSerializer(many=True, read_only=True):
|
||||||
id = IntegerField(label='ID', read_only=True)
|
id = IntegerField(label='ID', read_only=True)
|
||||||
name = CharField(label='name', max_length=100)
|
name = CharField(max_length=100)
|
||||||
""".strip()
|
""".strip()
|
||||||
|
|
||||||
|
|
||||||
HYPERLINKED_FLAT_REPR = """
|
HYPERLINKED_FLAT_REPR = """
|
||||||
TestSerializer():
|
TestSerializer():
|
||||||
url = HyperlinkedIdentityField(view_name='relationalmodel-detail')
|
url = HyperlinkedIdentityField(view_name='relationalmodel-detail')
|
||||||
foreign_key = HyperlinkedRelatedField(label='foreign key', queryset=ForeignKeyTargetModel.objects.all(), view_name='foreignkeytargetmodel-detail')
|
foreign_key = HyperlinkedRelatedField(queryset=ForeignKeyTargetModel.objects.all(), view_name='foreignkeytargetmodel-detail')
|
||||||
one_to_one = HyperlinkedRelatedField(label='one to one', queryset=OneToOneTargetModel.objects.all(), view_name='onetoonetargetmodel-detail')
|
one_to_one = HyperlinkedRelatedField(queryset=OneToOneTargetModel.objects.all(), view_name='onetoonetargetmodel-detail')
|
||||||
many_to_many = HyperlinkedRelatedField(label='many to many', many=True, queryset=ManyToManyTargetModel.objects.all(), view_name='manytomanytargetmodel-detail')
|
many_to_many = HyperlinkedRelatedField(many=True, queryset=ManyToManyTargetModel.objects.all(), view_name='manytomanytargetmodel-detail')
|
||||||
""".strip()
|
""".strip()
|
||||||
|
|
||||||
|
|
||||||
|
@ -116,13 +116,13 @@ TestSerializer():
|
||||||
url = HyperlinkedIdentityField(view_name='relationalmodel-detail')
|
url = HyperlinkedIdentityField(view_name='relationalmodel-detail')
|
||||||
foreign_key = NestedModelSerializer(read_only=True):
|
foreign_key = NestedModelSerializer(read_only=True):
|
||||||
id = IntegerField(label='ID', read_only=True)
|
id = IntegerField(label='ID', read_only=True)
|
||||||
name = CharField(label='name', max_length=100)
|
name = CharField(max_length=100)
|
||||||
one_to_one = NestedModelSerializer(read_only=True):
|
one_to_one = NestedModelSerializer(read_only=True):
|
||||||
id = IntegerField(label='ID', read_only=True)
|
id = IntegerField(label='ID', read_only=True)
|
||||||
name = CharField(label='name', max_length=100)
|
name = CharField(max_length=100)
|
||||||
many_to_many = NestedModelSerializer(many=True, read_only=True):
|
many_to_many = NestedModelSerializer(many=True, read_only=True):
|
||||||
id = IntegerField(label='ID', read_only=True)
|
id = IntegerField(label='ID', read_only=True)
|
||||||
name = CharField(label='name', max_length=100)
|
name = CharField(max_length=100)
|
||||||
""".strip()
|
""".strip()
|
||||||
|
|
||||||
|
|
||||||
|
@ -180,4 +180,4 @@ class TestSerializerMappings(TestCase):
|
||||||
# class Meta:
|
# class Meta:
|
||||||
# model = ManyToManyTargetModel
|
# model = ManyToManyTargetModel
|
||||||
# fields = ('id', 'name', 'reverse_many_to_many')
|
# fields = ('id', 'name', 'reverse_many_to_many')
|
||||||
# print repr(TestSerializer())
|
# print repr(TestSerializer())
|
||||||
|
|
Loading…
Reference in New Issue
Block a user