added tests, and fixed bugs

changed whole empty stuff - was just wrong or useless
This commit is contained in:
Ludwig Kraatz 2012-12-04 17:09:54 +01:00
parent 6f56b6279f
commit 2771b596d1
4 changed files with 73 additions and 19 deletions

View File

@ -344,12 +344,6 @@ except ImportError:
return datetime.datetime(**kw)
# empty does not exist in 1.3x
if django.VERSION >= (1, 4):
from django.utils.functional import empty
else:
empty = object()
# Markdown is optional
try:
import markdown

View File

@ -20,6 +20,12 @@ from rest_framework.compat import parse_date, parse_datetime
from rest_framework.compat import timezone
from urlparse import urlparse
class Empty(object):
"""
Placeholder for unset attributes.
Cannot use `None`, as that may be a valid value.
"""
pass
def is_simple_callable(obj):
"""
@ -530,7 +536,7 @@ class HyperlinkedRelatedField(RelatedField):
except:
raise ValueError("Hyperlinked field requires 'view_name' kwarg")
self.view_namespace = kwargs.pop('view_namespace', None)
self.view_namespace = kwargs.pop('view_namespace', Empty)
self.slug_field = kwargs.pop('slug_field', self.slug_field)
default_slug_kwarg = self.slug_url_kwarg or self.slug_field
@ -540,6 +546,15 @@ class HyperlinkedRelatedField(RelatedField):
self.format = kwargs.pop('format', None)
super(HyperlinkedRelatedField, self).__init__(*args, **kwargs)
def initialize(self, parent, field_name):
super(HyperlinkedRelatedField, self).initialize(parent, field_name)
if self.view_namespace is Empty:
self.view_namespace = getattr(self.parent.opts, 'view_namespace', None)
if self.view_namespace:
self.view_name = '%(namespace)s:%(name)s' % {'namespace': self.view_namespace, 'name': self.view_name}
def get_slug_field(self):
"""
Get the name of a slug field to be used to look up by slug.
@ -547,10 +562,7 @@ class HyperlinkedRelatedField(RelatedField):
return self.slug_field
def to_native(self, obj):
view_namespace = self.view_namespace
view_name = self.view_name
if view_namespace:
view_name = '%(namespace)s:%(name)s' % {'namespace': view_namespace, 'name': view_name}
request = self.context.get('request', None)
format = self.format or self.context.get('format', None)
@ -645,7 +657,7 @@ class HyperlinkedIdentityField(Field):
except:
raise ValueError("Hyperlinked Identity field requires 'view_name' kwarg")
self.view_namespace = kwargs.pop('view_namespace', None)
self.view_namespace = kwargs.pop('view_namespace', Empty)
self.format = kwargs.pop('format', None)
@ -654,15 +666,22 @@ class HyperlinkedIdentityField(Field):
self.pk_url_kwarg = kwargs.pop('pk_url_kwarg', self.pk_url_kwarg)
self.slug_url_kwarg = kwargs.pop('slug_url_kwarg', default_slug_kwarg)
super(HyperlinkedIdentityField, self).__init__(*args, **kwargs)
super(HyperlinkedIdentityField, self).__init__(*args, **kwargs)
def initialize(self, parent, field_name):
super(HyperlinkedIdentityField, self).initialize(parent, field_name)
if self.view_namespace is Empty:
self.view_namespace = getattr(self.parent.opts, 'view_namespace', None)
if self.view_namespace:
self.view_name = '%(namespace)s:%(name)s' % {'namespace': self.view_namespace, 'name': self.view_name}
def field_to_native(self, obj, field_name):
request = self.context.get('request', None)
format = self.format or self.context.get('format', None)
view_namespace = self.view_namespace
view_name = self.view_name
if view_namespace:
view_name = '%(namespace)s:%(name)s' % {'namespace': view_namespace, 'name': view_name}
kwargs = {self.pk_url_kwarg: obj.pk}
try:

View File

@ -6,7 +6,7 @@ from django.db import models
from django.forms import widgets
from django.utils.datastructures import SortedDict
from django.core.exceptions import ImproperlyConfigured
from rest_framework.compat import get_concrete_model, empty
from rest_framework.compat import get_concrete_model
# Note: We do the following so that users of the framework can use this style:
#
@ -69,7 +69,7 @@ def _get_declared_fields(bases, attrs):
return SortedDict(fields)
def _get_options_instance(bases, attrs):
options_class = Meta = empty
options_class = Meta = None
if '_options_class' in attrs:
options_class = attrs['_options_class']
else:
@ -77,7 +77,7 @@ def _get_options_instance(bases, attrs):
if hasattr(base, '_options_class'):
options_class = getattr(base, '_options_class')
break
if options_class is empty:
if options_class is None:
raise ImproperlyConfigured, 'A Serializer requires an "_options_class" attribute'
if 'Meta' in attrs:
@ -87,7 +87,7 @@ def _get_options_instance(bases, attrs):
if hasattr(base, 'Meta'):
Meta = getattr(base, 'Meta')
break
if Meta is empty:
if Meta is None:
raise ImproperlyConfigured, 'A Serializer requires an "Meta" attribute'
return options_class(Meta)

View File

@ -16,6 +16,16 @@ class BlogPostCommentSerializer(serializers.ModelSerializer):
model = BlogPostComment
fields = ('text', 'blog_post_url', 'url')
class NamespacedBlogPostCommentSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='blogpostcomment-detail')
text = serializers.CharField()
blog_post_url = serializers.HyperlinkedRelatedField(source='blog_post', view_name='blogpost-detail', view_namespace = None)
class Meta:
model = BlogPostComment
view_namespace = 'namespacetests'
fields = ('text', 'blog_post_url', 'url')
class PhotoSerializer(serializers.Serializer):
description = serializers.CharField()
@ -65,6 +75,14 @@ class BlogPostCommentDetail(generics.RetrieveAPIView):
model = BlogPostComment
serializer_class = BlogPostCommentSerializer
class NamespacedBlogPostCommentListCreate(generics.ListCreateAPIView):
model = BlogPostComment
serializer_class = NamespacedBlogPostCommentSerializer
class NamespacedBlogPostCommentDetail(generics.RetrieveAPIView):
model = BlogPostComment
serializer_class = NamespacedBlogPostCommentSerializer
class BlogPostDetail(generics.RetrieveAPIView):
model = BlogPost
@ -86,6 +104,8 @@ class OptionalRelationDetail(generics.RetrieveAPIView):
anchor_urls = patterns('',
url(r'^(?P<pk>\d+)/$', AnchorDetail.as_view(), name='anchor-detail'),
url(r'^manytomany/(?P<pk>\d+)/$', NamespacedManyToManyDetail.as_view(), name='manytomanymodel-detail'),
url(r'^comments/$', NamespacedBlogPostCommentListCreate.as_view(), name='blogpostcomment-list'),
url(r'^comments/(?P<pk>\d+)/$', NamespacedBlogPostCommentDetail.as_view(), name='blogpostcomment-detail'),
)
urlpatterns = patterns('',
@ -216,6 +236,7 @@ class TestCreateWithForeignKeys(TestCase):
"""
self.post = BlogPost.objects.create(title="Test post")
self.create_view = BlogPostCommentListCreate.as_view()
self.create_namespaced_view = NamespacedBlogPostCommentListCreate.as_view()
def test_create_comment(self):
@ -231,6 +252,26 @@ class TestCreateWithForeignKeys(TestCase):
self.assertEqual(self.post.blogpostcomment_set.count(), 1)
self.assertEqual(self.post.blogpostcomment_set.all()[0].text, 'A test comment')
def test_create_namespaced_comment(self):
data = {
'text': 'A test comment',
'blog_post_url': 'http://testserver/posts/1/'
}
response_data = {
'url': 'http://testserver/other/namespace/comments/1/',
'text': 'A test comment',
'blog_post_url': 'http://testserver/posts/1/'
}
request = factory.post('/other/namespace/comments/', data=data)
response = self.create_namespaced_view(request).render()
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response['Location'], 'http://testserver/other/namespace/comments/1/', msg='Not specified Namespace should be inherited from parents Options')
self.assertEqual(self.post.blogpostcomment_set.count(), 1)
self.assertEquals(response.data, response_data)
class TestCreateWithForeignKeysAndCustomSlug(TestCase):
urls = 'rest_framework.tests.hyperlinkedserializers'