django-rest-framework/tests/test_model_serializer.py

373 lines
15 KiB
Python
Raw Normal View History

2014-09-09 20:46:28 +04:00
"""
The `ModelSerializer` and `HyperlinkedModelSerializer` classes are essentially
shortcuts for automatically creating serializers based on a given model class.
These tests deal with ensuring that we correctly map the model fields onto
an appropriate set of serializer fields for each case.
"""
from django.core.exceptions import ImproperlyConfigured
2014-09-09 20:46:28 +04:00
from django.db import models
from django.test import TestCase
from rest_framework import serializers
2014-09-15 16:38:28 +04:00
def dedent(blocktext):
return '\n'.join([line[12:] for line in blocktext.splitlines()[1:-1]])
2014-09-17 17:11:53 +04:00
# Testing regular field mappings
2014-09-09 20:46:28 +04:00
2014-09-18 16:07:38 +04:00
class CustomField(models.Field):
pass
2014-09-09 20:46:28 +04:00
class RegularFieldsModel(models.Model):
auto_field = models.AutoField(primary_key=True)
big_integer_field = models.BigIntegerField()
2014-09-11 16:20:44 +04:00
boolean_field = models.BooleanField(default=False)
2014-09-09 20:46:28 +04:00
char_field = models.CharField(max_length=100)
comma_seperated_integer_field = models.CommaSeparatedIntegerField(max_length=100)
date_field = models.DateField()
datetime_field = models.DateTimeField()
decimal_field = models.DecimalField(max_digits=3, decimal_places=1)
email_field = models.EmailField(max_length=100)
float_field = models.FloatField()
integer_field = models.IntegerField()
null_boolean_field = models.NullBooleanField()
positive_integer_field = models.PositiveIntegerField()
positive_small_integer_field = models.PositiveSmallIntegerField()
slug_field = models.SlugField(max_length=100)
small_integer_field = models.SmallIntegerField()
text_field = models.TextField()
time_field = models.TimeField()
url_field = models.URLField(max_length=100)
2014-09-18 16:07:38 +04:00
custom_field = CustomField()
2014-09-09 20:46:28 +04:00
def method(self):
return 'method'
2014-09-09 20:46:28 +04:00
2014-09-15 16:38:28 +04:00
class TestRegularFieldMappings(TestCase):
def test_regular_fields(self):
2014-09-18 16:07:38 +04:00
"""
Model fields should map to their equivelent serializer fields.
"""
2014-09-15 16:38:28 +04:00
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = RegularFieldsModel
expected = dedent("""
TestSerializer():
auto_field = IntegerField(read_only=True)
big_integer_field = IntegerField()
boolean_field = BooleanField(default=False)
char_field = CharField(max_length=100)
comma_seperated_integer_field = CharField(max_length=100, validators=[<django.core.validators.RegexValidator object>])
date_field = DateField()
datetime_field = DateTimeField()
decimal_field = DecimalField(decimal_places=1, max_digits=3)
email_field = EmailField(max_length=100)
float_field = FloatField()
integer_field = IntegerField()
null_boolean_field = BooleanField(required=False)
positive_integer_field = IntegerField()
positive_small_integer_field = IntegerField()
slug_field = SlugField(max_length=100)
small_integer_field = IntegerField()
text_field = CharField()
time_field = TimeField()
url_field = URLField(max_length=100)
2014-09-18 16:07:38 +04:00
custom_field = ModelField(model_field=<tests.test_model_serializer.CustomField: custom_field>)
2014-09-15 16:38:28 +04:00
""")
self.assertEqual(repr(TestSerializer()), expected)
2014-09-09 20:46:28 +04:00
def test_method_field(self):
"""
Properties and methods on the model should be allowed as `Meta.fields`
values, and should map to `ReadOnlyField`.
"""
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = RegularFieldsModel
fields = ('auto_field', 'method')
expected = dedent("""
TestSerializer():
auto_field = IntegerField(read_only=True)
method = ReadOnlyField()
""")
self.assertEqual(repr(TestSerializer()), expected)
def test_pk_fields(self):
"""
Both `pk` and the actual primary key name are valid in `Meta.fields`.
"""
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = RegularFieldsModel
fields = ('pk', 'auto_field')
expected = dedent("""
TestSerializer():
pk = IntegerField(label='Auto field', read_only=True)
auto_field = IntegerField(read_only=True)
""")
self.assertEqual(repr(TestSerializer()), expected)
def test_extra_field_kwargs(self):
"""
Ensure `extra_kwargs` are passed to generated fields.
"""
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = RegularFieldsModel
fields = ('pk', 'char_field')
extra_kwargs = {'char_field': {'default': 'extra'}}
expected = dedent("""
TestSerializer():
pk = IntegerField(label='Auto field', read_only=True)
char_field = CharField(default='extra', max_length=100)
""")
self.assertEqual(repr(TestSerializer()), expected)
def test_invalid_field(self):
"""
Field names that do not map to a model field or relationship should
raise a configuration errror.
"""
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = RegularFieldsModel
fields = ('auto_field', 'invalid')
with self.assertRaises(ImproperlyConfigured) as excinfo:
TestSerializer()
expected = 'Field name `invalid` is not valid for model `ModelBase`.'
assert str(excinfo.exception) == expected
def test_missing_field(self):
2014-09-18 16:07:38 +04:00
"""
Fields that have been declared on the serializer class must be included
in the `Meta.fields` if it exists.
"""
class TestSerializer(serializers.ModelSerializer):
missing = serializers.ReadOnlyField()
class Meta:
model = RegularFieldsModel
fields = ('auto_field',)
with self.assertRaises(ImproperlyConfigured) as excinfo:
TestSerializer()
expected = (
'Field `missing` has been declared on serializer '
'`TestSerializer`, but is missing from `Meta.fields`.'
)
assert str(excinfo.exception) == expected
2014-09-09 20:46:28 +04:00
2014-09-17 17:11:53 +04:00
# Testing relational field mappings
2014-09-09 20:46:28 +04:00
2014-09-11 16:20:44 +04:00
class ForeignKeyTargetModel(models.Model):
name = models.CharField(max_length=100)
2014-09-09 20:46:28 +04:00
2014-09-11 16:20:44 +04:00
class ManyToManyTargetModel(models.Model):
name = models.CharField(max_length=100)
2014-09-09 20:46:28 +04:00
2014-09-11 16:20:44 +04:00
class OneToOneTargetModel(models.Model):
name = models.CharField(max_length=100)
2014-09-09 20:46:28 +04:00
2014-09-15 17:05:58 +04:00
class ThroughTargetModel(models.Model):
name = models.CharField(max_length=100)
class Supplementary(models.Model):
extra = models.IntegerField()
forwards = models.ForeignKey('ThroughTargetModel')
backwards = models.ForeignKey('RelationalModel')
2014-09-09 20:46:28 +04:00
class RelationalModel(models.Model):
2014-09-13 00:32:20 +04:00
foreign_key = models.ForeignKey(ForeignKeyTargetModel, related_name='reverse_foreign_key')
many_to_many = models.ManyToManyField(ManyToManyTargetModel, related_name='reverse_many_to_many')
one_to_one = models.OneToOneField(OneToOneTargetModel, related_name='reverse_one_to_one')
2014-09-15 17:05:58 +04:00
through = models.ManyToManyField(ThroughTargetModel, through=Supplementary, related_name='reverse_through')
2014-09-09 20:46:28 +04:00
2014-09-15 16:38:28 +04:00
class TestRelationalFieldMappings(TestCase):
2014-09-18 17:23:00 +04:00
def test_pk_relations(self):
2014-09-09 20:46:28 +04:00
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = RelationalModel
2014-09-15 16:38:28 +04:00
expected = dedent("""
TestSerializer():
id = IntegerField(label='ID', read_only=True)
foreign_key = PrimaryKeyRelatedField(queryset=ForeignKeyTargetModel.objects.all())
one_to_one = PrimaryKeyRelatedField(queryset=OneToOneTargetModel.objects.all())
many_to_many = PrimaryKeyRelatedField(many=True, queryset=ManyToManyTargetModel.objects.all())
2014-09-15 17:05:58 +04:00
through = PrimaryKeyRelatedField(many=True, read_only=True)
2014-09-15 16:38:28 +04:00
""")
self.assertEqual(repr(TestSerializer()), expected)
2014-09-09 20:46:28 +04:00
2014-09-18 17:23:00 +04:00
def test_nested_relations(self):
2014-09-09 20:46:28 +04:00
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = RelationalModel
depth = 1
2014-09-15 16:38:28 +04:00
expected = dedent("""
TestSerializer():
id = IntegerField(label='ID', read_only=True)
2014-09-18 14:20:56 +04:00
foreign_key = NestedSerializer(read_only=True):
2014-09-15 16:38:28 +04:00
id = IntegerField(label='ID', read_only=True)
name = CharField(max_length=100)
2014-09-18 14:20:56 +04:00
one_to_one = NestedSerializer(read_only=True):
2014-09-15 16:38:28 +04:00
id = IntegerField(label='ID', read_only=True)
name = CharField(max_length=100)
2014-09-18 14:20:56 +04:00
many_to_many = NestedSerializer(many=True, read_only=True):
2014-09-15 16:38:28 +04:00
id = IntegerField(label='ID', read_only=True)
name = CharField(max_length=100)
2014-09-18 14:20:56 +04:00
through = NestedSerializer(many=True, read_only=True):
2014-09-15 17:05:58 +04:00
id = IntegerField(label='ID', read_only=True)
name = CharField(max_length=100)
2014-09-15 16:38:28 +04:00
""")
self.assertEqual(repr(TestSerializer()), expected)
2014-09-09 20:46:28 +04:00
2014-09-18 17:23:00 +04:00
def test_hyperlinked_relations(self):
2014-09-09 20:46:28 +04:00
class TestSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = RelationalModel
2014-09-15 16:38:28 +04:00
expected = dedent("""
TestSerializer():
url = HyperlinkedIdentityField(view_name='relationalmodel-detail')
foreign_key = HyperlinkedRelatedField(queryset=ForeignKeyTargetModel.objects.all(), view_name='foreignkeytargetmodel-detail')
one_to_one = HyperlinkedRelatedField(queryset=OneToOneTargetModel.objects.all(), view_name='onetoonetargetmodel-detail')
many_to_many = HyperlinkedRelatedField(many=True, queryset=ManyToManyTargetModel.objects.all(), view_name='manytomanytargetmodel-detail')
2014-09-15 17:05:58 +04:00
through = HyperlinkedRelatedField(many=True, read_only=True, view_name='throughtargetmodel-detail')
2014-09-15 16:38:28 +04:00
""")
self.assertEqual(repr(TestSerializer()), expected)
2014-09-09 20:46:28 +04:00
2014-09-18 17:23:00 +04:00
def test_nested_hyperlinked_relations(self):
2014-09-09 20:46:28 +04:00
class TestSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = RelationalModel
depth = 1
2014-09-15 16:38:28 +04:00
expected = dedent("""
TestSerializer():
url = HyperlinkedIdentityField(view_name='relationalmodel-detail')
2014-09-18 14:20:56 +04:00
foreign_key = NestedSerializer(read_only=True):
2014-09-15 16:55:09 +04:00
url = HyperlinkedIdentityField(view_name='foreignkeytargetmodel-detail')
2014-09-15 16:38:28 +04:00
name = CharField(max_length=100)
2014-09-18 14:20:56 +04:00
one_to_one = NestedSerializer(read_only=True):
2014-09-15 16:55:09 +04:00
url = HyperlinkedIdentityField(view_name='onetoonetargetmodel-detail')
2014-09-15 16:38:28 +04:00
name = CharField(max_length=100)
2014-09-18 14:20:56 +04:00
many_to_many = NestedSerializer(many=True, read_only=True):
2014-09-15 16:55:09 +04:00
url = HyperlinkedIdentityField(view_name='manytomanytargetmodel-detail')
2014-09-15 16:38:28 +04:00
name = CharField(max_length=100)
2014-09-18 14:20:56 +04:00
through = NestedSerializer(many=True, read_only=True):
2014-09-15 17:05:58 +04:00
url = HyperlinkedIdentityField(view_name='throughtargetmodel-detail')
name = CharField(max_length=100)
2014-09-15 16:48:03 +04:00
""")
self.assertEqual(repr(TestSerializer()), expected)
2014-09-18 17:23:00 +04:00
def test_pk_reverse_foreign_key(self):
2014-09-15 16:48:03 +04:00
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = ForeignKeyTargetModel
fields = ('id', 'name', 'reverse_foreign_key')
expected = dedent("""
TestSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(max_length=100)
reverse_foreign_key = PrimaryKeyRelatedField(many=True, queryset=RelationalModel.objects.all())
""")
2014-09-15 16:38:28 +04:00
self.assertEqual(repr(TestSerializer()), expected)
2014-09-13 00:32:20 +04:00
2014-09-18 17:23:00 +04:00
def test_pk_reverse_one_to_one(self):
2014-09-15 16:48:03 +04:00
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = OneToOneTargetModel
fields = ('id', 'name', 'reverse_one_to_one')
expected = dedent("""
TestSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(max_length=100)
reverse_one_to_one = PrimaryKeyRelatedField(queryset=RelationalModel.objects.all())
""")
self.assertEqual(repr(TestSerializer()), expected)
2014-09-18 17:23:00 +04:00
def test_pk_reverse_many_to_many(self):
2014-09-15 16:48:03 +04:00
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = ManyToManyTargetModel
fields = ('id', 'name', 'reverse_many_to_many')
expected = dedent("""
TestSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(max_length=100)
reverse_many_to_many = PrimaryKeyRelatedField(many=True, queryset=RelationalModel.objects.all())
""")
self.assertEqual(repr(TestSerializer()), expected)
2014-09-15 17:05:58 +04:00
2014-09-18 17:23:00 +04:00
def test_pk_reverse_through(self):
2014-09-15 17:05:58 +04:00
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = ThroughTargetModel
fields = ('id', 'name', 'reverse_through')
expected = dedent("""
TestSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(max_length=100)
reverse_through = PrimaryKeyRelatedField(many=True, read_only=True)
""")
self.assertEqual(repr(TestSerializer()), expected)
2014-09-18 17:23:00 +04:00
class TestIntegration(TestCase):
def setUp(self):
self.foreign_key_target = ForeignKeyTargetModel.objects.create(
name='foreign_key'
)
self.one_to_one_target = OneToOneTargetModel.objects.create(
name='one_to_one'
)
self.many_to_many_targets = [
ManyToManyTargetModel.objects.create(
name='many_to_many (%d)' % idx
) for idx in range(3)
]
self.instance = RelationalModel.objects.create(
foreign_key=self.foreign_key_target,
one_to_one=self.one_to_one_target,
)
self.instance.many_to_many = self.many_to_many_targets
self.instance.save()
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = RelationalModel
self.serializer_cls = TestSerializer
def test_pk_relationship_representations(self):
serializer = self.serializer_cls(self.instance)
expected = {
'id': self.instance.pk,
'foreign_key': self.foreign_key_target.pk,
'one_to_one': self.one_to_one_target.pk,
'many_to_many': [item.pk for item in self.many_to_many_targets],
'through': []
}
self.assertEqual(serializer.data, expected)