aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_relations.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_relations.py')
-rw-r--r--tests/test_relations.py419
1 files changed, 279 insertions, 140 deletions
diff --git a/tests/test_relations.py b/tests/test_relations.py
index bc1db69f..c29618ce 100644
--- a/tests/test_relations.py
+++ b/tests/test_relations.py
@@ -1,149 +1,288 @@
-"""
-General tests for relational fields.
-"""
-from __future__ import unicode_literals
-from django import get_version
-from django.db import models
-from django.test import TestCase
-from django.utils import unittest
+from .utils import mock_reverse, fail_reverse, BadType, MockObject, MockQueryset
+from django.core.exceptions import ImproperlyConfigured, ValidationError
from rest_framework import serializers
-from tests.models import BlogPost
-
-
-class NullModel(models.Model):
- pass
-
-
-class FieldTests(TestCase):
- def test_pk_related_field_with_empty_string(self):
- """
- Regression test for #446
-
- https://github.com/tomchristie/django-rest-framework/issues/446
- """
- field = serializers.PrimaryKeyRelatedField(queryset=NullModel.objects.all())
- self.assertRaises(serializers.ValidationError, field.from_native, '')
- self.assertRaises(serializers.ValidationError, field.from_native, [])
-
- def test_hyperlinked_related_field_with_empty_string(self):
- field = serializers.HyperlinkedRelatedField(queryset=NullModel.objects.all(), view_name='')
- self.assertRaises(serializers.ValidationError, field.from_native, '')
- self.assertRaises(serializers.ValidationError, field.from_native, [])
-
- def test_slug_related_field_with_empty_string(self):
- field = serializers.SlugRelatedField(queryset=NullModel.objects.all(), slug_field='pk')
- self.assertRaises(serializers.ValidationError, field.from_native, '')
- self.assertRaises(serializers.ValidationError, field.from_native, [])
-
-
-class TestManyRelatedMixin(TestCase):
- def test_missing_many_to_many_related_field(self):
- '''
- Regression test for #632
-
- https://github.com/tomchristie/django-rest-framework/pull/632
- '''
- field = serializers.RelatedField(many=True, read_only=False)
-
- into = {}
- field.field_from_native({}, None, 'field_name', into)
- self.assertEqual(into['field_name'], [])
-
-
-# Regression tests for #694 (`source` attribute on related fields)
-
-class RelatedFieldSourceTests(TestCase):
- def test_related_manager_source(self):
- """
- Relational fields should be able to use manager-returning methods as their source.
- """
- BlogPost.objects.create(title='blah')
- field = serializers.RelatedField(many=True, source='get_blogposts_manager')
-
- class ClassWithManagerMethod(object):
- def get_blogposts_manager(self):
- return BlogPost.objects
-
- obj = ClassWithManagerMethod()
- value = field.field_to_native(obj, 'field_name')
- self.assertEqual(value, ['BlogPost object'])
-
- def test_related_queryset_source(self):
- """
- Relational fields should be able to use queryset-returning methods as their source.
- """
- BlogPost.objects.create(title='blah')
- field = serializers.RelatedField(many=True, source='get_blogposts_queryset')
-
- class ClassWithQuerysetMethod(object):
- def get_blogposts_queryset(self):
- return BlogPost.objects.all()
-
- obj = ClassWithQuerysetMethod()
- value = field.field_to_native(obj, 'field_name')
- self.assertEqual(value, ['BlogPost object'])
-
- def test_dotted_source(self):
- """
- Source argument should support dotted.source notation.
+from rest_framework.test import APISimpleTestCase
+import pytest
+
+
+class TestStringRelatedField(APISimpleTestCase):
+ def setUp(self):
+ self.instance = MockObject(pk=1, name='foo')
+ self.field = serializers.StringRelatedField()
+
+ def test_string_related_representation(self):
+ representation = self.field.to_representation(self.instance)
+ assert representation == '<MockObject name=foo, pk=1>'
+
+
+class TestPrimaryKeyRelatedField(APISimpleTestCase):
+ def setUp(self):
+ self.queryset = MockQueryset([
+ MockObject(pk=1, name='foo'),
+ MockObject(pk=2, name='bar'),
+ MockObject(pk=3, name='baz')
+ ])
+ self.instance = self.queryset.items[2]
+ self.field = serializers.PrimaryKeyRelatedField(queryset=self.queryset)
+
+ def test_pk_related_lookup_exists(self):
+ instance = self.field.to_internal_value(self.instance.pk)
+ assert instance is self.instance
+
+ def test_pk_related_lookup_does_not_exist(self):
+ with pytest.raises(ValidationError) as excinfo:
+ self.field.to_internal_value(4)
+ msg = excinfo.value.messages[0]
+ assert msg == "Invalid pk '4' - object does not exist."
+
+ def test_pk_related_lookup_invalid_type(self):
+ with pytest.raises(ValidationError) as excinfo:
+ self.field.to_internal_value(BadType())
+ msg = excinfo.value.messages[0]
+ assert msg == 'Incorrect type. Expected pk value, received BadType.'
+
+ def test_pk_representation(self):
+ representation = self.field.to_representation(self.instance)
+ assert representation == self.instance.pk
+
+
+class TestHyperlinkedIdentityField(APISimpleTestCase):
+ def setUp(self):
+ self.instance = MockObject(pk=1, name='foo')
+ self.field = serializers.HyperlinkedIdentityField(view_name='example')
+ self.field.reverse = mock_reverse
+ self.field.context = {'request': True}
+
+ def test_representation(self):
+ representation = self.field.to_representation(self.instance)
+ assert representation == 'http://example.org/example/1/'
+
+ def test_representation_unsaved_object(self):
+ representation = self.field.to_representation(MockObject(pk=None))
+ assert representation is None
+
+ def test_representation_with_format(self):
+ self.field.context['format'] = 'xml'
+ representation = self.field.to_representation(self.instance)
+ assert representation == 'http://example.org/example/1.xml/'
+
+ def test_improperly_configured(self):
"""
- BlogPost.objects.create(title='blah')
- field = serializers.RelatedField(many=True, source='a.b.c')
-
- class ClassWithQuerysetMethod(object):
- a = {
- 'b': {
- 'c': BlogPost.objects.all()
- }
- }
-
- obj = ClassWithQuerysetMethod()
- value = field.field_to_native(obj, 'field_name')
- self.assertEqual(value, ['BlogPost object'])
-
- # Regression for #1129
- def test_exception_for_incorect_fk(self):
+ If a matching view cannot be reversed with the given instance,
+ the the user has misconfigured something, as the URL conf and the
+ hyperlinked field do not match.
"""
- Check that the exception message are correct if the source field
- doesn't exist.
- """
- from tests.models import ManyToManySource
-
- class Meta:
- model = ManyToManySource
+ self.field.reverse = fail_reverse
+ with pytest.raises(ImproperlyConfigured):
+ self.field.to_representation(self.instance)
- attrs = {
- 'name': serializers.SlugRelatedField(
- slug_field='name', source='banzai'),
- 'Meta': Meta,
- }
- TestSerializer = type(
- str('TestSerializer'),
- (serializers.ModelSerializer,),
- attrs
- )
- with self.assertRaises(AttributeError):
- TestSerializer(data={'name': 'foo'})
-
-
-@unittest.skipIf(get_version() < '1.6.0', 'Upstream behaviour changed in v1.6')
-class RelatedFieldChoicesTests(TestCase):
+class TestHyperlinkedIdentityFieldWithFormat(APISimpleTestCase):
"""
- Tests for #1408 "Web browseable API doesn't have blank option on drop down list box"
- https://github.com/tomchristie/django-rest-framework/issues/1408
- """
- def test_blank_option_is_added_to_choice_if_required_equals_false(self):
- """
+ Tests for a hyperlinked identity field that has a `format` set,
+ which enforces that alternate formats are never linked too.
- """
- post = BlogPost(title="Checking blank option is added")
- post.save()
-
- queryset = BlogPost.objects.all()
- field = serializers.RelatedField(required=False, queryset=queryset)
+ Eg. If your API includes some endpoints that accept both `.xml` and `.json`,
+ but other endpoints that only accept `.json`, we allow for hyperlinked
+ relationships that enforce only a single suffix type.
+ """
- choice_count = BlogPost.objects.count()
- widget_count = len(field.widget.choices)
+ def setUp(self):
+ self.instance = MockObject(pk=1, name='foo')
+ self.field = serializers.HyperlinkedIdentityField(view_name='example', format='json')
+ self.field.reverse = mock_reverse
+ self.field.context = {'request': True}
+
+ def test_representation(self):
+ representation = self.field.to_representation(self.instance)
+ assert representation == 'http://example.org/example/1/'
+
+ def test_representation_with_format(self):
+ self.field.context['format'] = 'xml'
+ representation = self.field.to_representation(self.instance)
+ assert representation == 'http://example.org/example/1.json/'
+
+
+class TestSlugRelatedField(APISimpleTestCase):
+ def setUp(self):
+ self.queryset = MockQueryset([
+ MockObject(pk=1, name='foo'),
+ MockObject(pk=2, name='bar'),
+ MockObject(pk=3, name='baz')
+ ])
+ self.instance = self.queryset.items[2]
+ self.field = serializers.SlugRelatedField(
+ slug_field='name', queryset=self.queryset
+ )
- self.assertEqual(widget_count, choice_count + 1, 'BLANK_CHOICE_DASH option should have been added')
+ def test_slug_related_lookup_exists(self):
+ instance = self.field.to_internal_value(self.instance.name)
+ assert instance is self.instance
+
+ def test_slug_related_lookup_does_not_exist(self):
+ with pytest.raises(ValidationError) as excinfo:
+ self.field.to_internal_value('doesnotexist')
+ msg = excinfo.value.messages[0]
+ assert msg == 'Object with name=doesnotexist does not exist.'
+
+ def test_slug_related_lookup_invalid_type(self):
+ with pytest.raises(ValidationError) as excinfo:
+ self.field.to_internal_value(BadType())
+ msg = excinfo.value.messages[0]
+ assert msg == 'Invalid value.'
+
+ def test_representation(self):
+ representation = self.field.to_representation(self.instance)
+ assert representation == self.instance.name
+
+# Older tests, for review...
+
+# """
+# General tests for relational fields.
+# """
+# from __future__ import unicode_literals
+# from django import get_version
+# from django.db import models
+# from django.test import TestCase
+# from django.utils import unittest
+# from rest_framework import serializers
+# from tests.models import BlogPost
+
+
+# class NullModel(models.Model):
+# pass
+
+
+# class FieldTests(TestCase):
+# def test_pk_related_field_with_empty_string(self):
+# """
+# Regression test for #446
+
+# https://github.com/tomchristie/django-rest-framework/issues/446
+# """
+# field = serializers.PrimaryKeyRelatedField(queryset=NullModel.objects.all())
+# self.assertRaises(serializers.ValidationError, field.to_primative, '')
+# self.assertRaises(serializers.ValidationError, field.to_primative, [])
+
+# def test_hyperlinked_related_field_with_empty_string(self):
+# field = serializers.HyperlinkedRelatedField(queryset=NullModel.objects.all(), view_name='')
+# self.assertRaises(serializers.ValidationError, field.to_primative, '')
+# self.assertRaises(serializers.ValidationError, field.to_primative, [])
+
+# def test_slug_related_field_with_empty_string(self):
+# field = serializers.SlugRelatedField(queryset=NullModel.objects.all(), slug_field='pk')
+# self.assertRaises(serializers.ValidationError, field.to_primative, '')
+# self.assertRaises(serializers.ValidationError, field.to_primative, [])
+
+
+# class TestManyRelatedMixin(TestCase):
+# def test_missing_many_to_many_related_field(self):
+# '''
+# Regression test for #632
+
+# https://github.com/tomchristie/django-rest-framework/pull/632
+# '''
+# field = serializers.RelatedField(many=True, read_only=False)
+
+# into = {}
+# field.field_from_native({}, None, 'field_name', into)
+# self.assertEqual(into['field_name'], [])
+
+
+# # Regression tests for #694 (`source` attribute on related fields)
+
+# class RelatedFieldSourceTests(TestCase):
+# def test_related_manager_source(self):
+# """
+# Relational fields should be able to use manager-returning methods as their source.
+# """
+# BlogPost.objects.create(title='blah')
+# field = serializers.RelatedField(many=True, source='get_blogposts_manager')
+
+# class ClassWithManagerMethod(object):
+# def get_blogposts_manager(self):
+# return BlogPost.objects
+
+# obj = ClassWithManagerMethod()
+# value = field.field_to_native(obj, 'field_name')
+# self.assertEqual(value, ['BlogPost object'])
+
+# def test_related_queryset_source(self):
+# """
+# Relational fields should be able to use queryset-returning methods as their source.
+# """
+# BlogPost.objects.create(title='blah')
+# field = serializers.RelatedField(many=True, source='get_blogposts_queryset')
+
+# class ClassWithQuerysetMethod(object):
+# def get_blogposts_queryset(self):
+# return BlogPost.objects.all()
+
+# obj = ClassWithQuerysetMethod()
+# value = field.field_to_native(obj, 'field_name')
+# self.assertEqual(value, ['BlogPost object'])
+
+# def test_dotted_source(self):
+# """
+# Source argument should support dotted.source notation.
+# """
+# BlogPost.objects.create(title='blah')
+# field = serializers.RelatedField(many=True, source='a.b.c')
+
+# class ClassWithQuerysetMethod(object):
+# a = {
+# 'b': {
+# 'c': BlogPost.objects.all()
+# }
+# }
+
+# obj = ClassWithQuerysetMethod()
+# value = field.field_to_native(obj, 'field_name')
+# self.assertEqual(value, ['BlogPost object'])
+
+# # Regression for #1129
+# def test_exception_for_incorect_fk(self):
+# """
+# Check that the exception message are correct if the source field
+# doesn't exist.
+# """
+# from tests.models import ManyToManySource
+
+# class Meta:
+# model = ManyToManySource
+
+# attrs = {
+# 'name': serializers.SlugRelatedField(
+# slug_field='name', source='banzai'),
+# 'Meta': Meta,
+# }
+
+# TestSerializer = type(
+# str('TestSerializer'),
+# (serializers.ModelSerializer,),
+# attrs
+# )
+# with self.assertRaises(AttributeError):
+# TestSerializer(data={'name': 'foo'})
+
+
+# @unittest.skipIf(get_version() < '1.6.0', 'Upstream behaviour changed in v1.6')
+# class RelatedFieldChoicesTests(TestCase):
+# """
+# Tests for #1408 "Web browseable API doesn't have blank option on drop down list box"
+# https://github.com/tomchristie/django-rest-framework/issues/1408
+# """
+# def test_blank_option_is_added_to_choice_if_required_equals_false(self):
+# """
+
+# """
+# post = BlogPost(title="Checking blank option is added")
+# post.save()
+
+# queryset = BlogPost.objects.all()
+# field = serializers.RelatedField(required=False, queryset=queryset)
+
+# choice_count = BlogPost.objects.count()
+# widget_count = len(field.widget.choices)
+
+# self.assertEqual(widget_count, choice_count + 1, 'BLANK_CHOICE_DASH option should have been added')