aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Christie2012-11-02 14:08:49 -0700
committerTom Christie2012-11-02 14:08:49 -0700
commitb468dd6271d95791cc05d5213042f1801145609f (patch)
tree06bf74cf4b4a9651c1a03b2f7b92ac4b9a5e1ef7
parentdafbbdc5bf781267a824f740791d5e585e4d93d9 (diff)
parent8ec54e6a9fe8e46955098ca8e7d728d8bb4f7053 (diff)
downloaddjango-rest-framework-b468dd6271d95791cc05d5213042f1801145609f.tar.bz2
Merge pull request #359 from tomchristie/relationship_tests
Relationship tests
-rw-r--r--rest_framework/fields.py15
-rwxr-xr-xrest_framework/runtests/runcoverage.py4
-rw-r--r--rest_framework/tests/pk_relations.py187
3 files changed, 202 insertions, 4 deletions
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index 515ccbcf..5b28463c 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -346,7 +346,7 @@ class ManyRelatedField(ManyRelatedMixin, RelatedField):
"""
Base class for related model managers.
- If not overridden, this represents a to-many relatinship, using the unicode
+ If not overridden, this represents a to-many relationship, using the unicode
representations of the target, and is read-only.
"""
pass
@@ -385,7 +385,8 @@ class PrimaryKeyRelatedField(RelatedField):
try:
return self.queryset.get(pk=data)
except ObjectDoesNotExist:
- raise ValidationError('Invalid hyperlink - object does not exist.')
+ msg = "Invalid pk '%s' - object does not exist." % smart_unicode(data)
+ raise ValidationError(msg)
def field_to_native(self, obj, field_name):
try:
@@ -432,6 +433,16 @@ class ManyPrimaryKeyRelatedField(ManyRelatedField):
# Forward relationship
return [self.to_native(item.pk) for item in queryset.all()]
+ def from_native(self, data):
+ if self.queryset is None:
+ raise Exception('Writable related fields must include a `queryset` argument')
+
+ try:
+ return self.queryset.get(pk=data)
+ except ObjectDoesNotExist:
+ msg = "Invalid pk '%s' - object does not exist." % smart_unicode(data)
+ raise ValidationError(msg)
+
### Slug relationships
diff --git a/rest_framework/runtests/runcoverage.py b/rest_framework/runtests/runcoverage.py
index ea2e3d45..0ce379eb 100755
--- a/rest_framework/runtests/runcoverage.py
+++ b/rest_framework/runtests/runcoverage.py
@@ -32,10 +32,10 @@ def main():
'Function-based test runners are deprecated. Test runners should be classes with a run_tests() method.',
DeprecationWarning
)
- failures = TestRunner(['rest_framework'])
+ failures = TestRunner(['tests'])
else:
test_runner = TestRunner()
- failures = test_runner.run_tests(['rest_framework'])
+ failures = test_runner.run_tests(['tests'])
cov.stop()
# Discover the list of all modules that we should test coverage for
diff --git a/rest_framework/tests/pk_relations.py b/rest_framework/tests/pk_relations.py
new file mode 100644
index 00000000..9095dcd8
--- /dev/null
+++ b/rest_framework/tests/pk_relations.py
@@ -0,0 +1,187 @@
+from django.db import models
+from django.test import TestCase
+from rest_framework import serializers
+
+
+# ManyToMany
+
+class ManyToManyTarget(models.Model):
+ name = models.CharField(max_length=100)
+
+
+class ManyToManySource(models.Model):
+ name = models.CharField(max_length=100)
+ targets = models.ManyToManyField(ManyToManyTarget, related_name='sources')
+
+
+class ManyToManyTargetSerializer(serializers.ModelSerializer):
+ sources = serializers.ManyPrimaryKeyRelatedField(queryset=ManyToManySource.objects.all())
+
+ class Meta:
+ model = ManyToManyTarget
+
+
+class ManyToManySourceSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = ManyToManySource
+
+
+# ForeignKey
+
+class ForeignKeyTarget(models.Model):
+ name = models.CharField(max_length=100)
+
+
+class ForeignKeySource(models.Model):
+ name = models.CharField(max_length=100)
+ target = models.ForeignKey(ForeignKeyTarget, related_name='sources')
+
+
+class ForeignKeyTargetSerializer(serializers.ModelSerializer):
+ sources = serializers.ManyPrimaryKeyRelatedField(read_only=True)
+
+ class Meta:
+ model = ForeignKeyTarget
+
+
+class ForeignKeySourceSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = ForeignKeySource
+
+
+# TODO: Add test that .data cannot be accessed prior to .is_valid
+
+class PrimaryKeyManyToManyTests(TestCase):
+ def setUp(self):
+ for idx in range(1, 4):
+ target = ManyToManyTarget(name='target-%d' % idx)
+ target.save()
+ source = ManyToManySource(name='source-%d' % idx)
+ source.save()
+ for target in ManyToManyTarget.objects.all():
+ source.targets.add(target)
+
+ def test_many_to_many_retrieve(self):
+ queryset = ManyToManySource.objects.all()
+ serializer = ManyToManySourceSerializer(instance=queryset)
+ expected = [
+ {'id': 1, 'name': u'source-1', 'targets': [1]},
+ {'id': 2, 'name': u'source-2', 'targets': [1, 2]},
+ {'id': 3, 'name': u'source-3', 'targets': [1, 2, 3]}
+ ]
+ self.assertEquals(serializer.data, expected)
+
+ def test_reverse_many_to_many_retrieve(self):
+ queryset = ManyToManyTarget.objects.all()
+ serializer = ManyToManyTargetSerializer(instance=queryset)
+ expected = [
+ {'id': 1, 'name': u'target-1', 'sources': [1, 2, 3]},
+ {'id': 2, 'name': u'target-2', 'sources': [2, 3]},
+ {'id': 3, 'name': u'target-3', 'sources': [3]}
+ ]
+ self.assertEquals(serializer.data, expected)
+
+ def test_many_to_many_update(self):
+ data = {'id': 1, 'name': u'source-1', 'targets': [1, 2, 3]}
+ instance = ManyToManySource.objects.get(pk=1)
+ serializer = ManyToManySourceSerializer(data, instance=instance)
+ self.assertTrue(serializer.is_valid())
+ self.assertEquals(serializer.data, data)
+ serializer.save()
+
+ # Ensure source 1 is updated, and everything else is as expected
+ queryset = ManyToManySource.objects.all()
+ serializer = ManyToManySourceSerializer(instance=queryset)
+ expected = [
+ {'id': 1, 'name': u'source-1', 'targets': [1, 2, 3]},
+ {'id': 2, 'name': u'source-2', 'targets': [1, 2]},
+ {'id': 3, 'name': u'source-3', 'targets': [1, 2, 3]}
+ ]
+ self.assertEquals(serializer.data, expected)
+
+ def test_reverse_many_to_many_update(self):
+ data = {'id': 1, 'name': u'target-1', 'sources': [1]}
+ instance = ManyToManyTarget.objects.get(pk=1)
+ serializer = ManyToManyTargetSerializer(data, instance=instance)
+ self.assertTrue(serializer.is_valid())
+ self.assertEquals(serializer.data, data)
+ serializer.save()
+
+ # Ensure target 1 is updated, and everything else is as expected
+ queryset = ManyToManyTarget.objects.all()
+ serializer = ManyToManyTargetSerializer(instance=queryset)
+ expected = [
+ {'id': 1, 'name': u'target-1', 'sources': [1]},
+ {'id': 2, 'name': u'target-2', 'sources': [2, 3]},
+ {'id': 3, 'name': u'target-3', 'sources': [3]}
+ ]
+ self.assertEquals(serializer.data, expected)
+
+
+class PrimaryKeyForeignKeyTests(TestCase):
+ def setUp(self):
+ target = ForeignKeyTarget(name='target-1')
+ target.save()
+ new_target = ForeignKeyTarget(name='target-2')
+ new_target.save()
+ for idx in range(1, 4):
+ source = ForeignKeySource(name='source-%d' % idx, target=target)
+ source.save()
+
+ def test_foreign_key_retrieve(self):
+ queryset = ForeignKeySource.objects.all()
+ serializer = ForeignKeySourceSerializer(instance=queryset)
+ expected = [
+ {'id': 1, 'name': u'source-1', 'target': 1},
+ {'id': 2, 'name': u'source-2', 'target': 1},
+ {'id': 3, 'name': u'source-3', 'target': 1}
+ ]
+ self.assertEquals(serializer.data, expected)
+
+ def test_reverse_foreign_key_retrieve(self):
+ queryset = ForeignKeyTarget.objects.all()
+ serializer = ForeignKeyTargetSerializer(instance=queryset)
+ expected = [
+ {'id': 1, 'name': u'target-1', 'sources': [1, 2, 3]},
+ {'id': 2, 'name': u'target-2', 'sources': []},
+ ]
+ self.assertEquals(serializer.data, expected)
+
+ def test_foreign_key_update(self):
+ data = {'id': 1, 'name': u'source-1', 'target': 2}
+ instance = ForeignKeySource.objects.get(pk=1)
+ serializer = ForeignKeySourceSerializer(data, instance=instance)
+ self.assertTrue(serializer.is_valid())
+ self.assertEquals(serializer.data, data)
+ serializer.save()
+
+ # # Ensure source 1 is updated, and everything else is as expected
+ queryset = ForeignKeySource.objects.all()
+ serializer = ForeignKeySourceSerializer(instance=queryset)
+ expected = [
+ {'id': 1, 'name': u'source-1', 'target': 2},
+ {'id': 2, 'name': u'source-2', 'target': 1},
+ {'id': 3, 'name': u'source-3', 'target': 1}
+ ]
+ self.assertEquals(serializer.data, expected)
+
+ # reverse foreign keys MUST be read_only
+ # In the general case they do not provide .remove() or .clear()
+ # and cannot be arbitrarily set.
+
+ # def test_reverse_foreign_key_update(self):
+ # data = {'id': 1, 'name': u'target-1', 'sources': [1]}
+ # instance = ForeignKeyTarget.objects.get(pk=1)
+ # serializer = ForeignKeyTargetSerializer(data, instance=instance)
+ # self.assertTrue(serializer.is_valid())
+ # self.assertEquals(serializer.data, data)
+ # serializer.save()
+
+ # # Ensure target 1 is updated, and everything else is as expected
+ # queryset = ForeignKeyTarget.objects.all()
+ # serializer = ForeignKeyTargetSerializer(instance=queryset)
+ # expected = [
+ # {'id': 1, 'name': u'target-1', 'sources': [1]},
+ # {'id': 2, 'name': u'target-2', 'sources': []},
+ # ]
+ # self.assertEquals(serializer.data, expected)