mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-07-25 23:50:01 +03:00
Merge 88d1aa24bc
into a463ddbb37
This commit is contained in:
commit
8c5ab053f4
|
@ -314,6 +314,7 @@ By default, `HyperlinkedRelatedField` is read-write, although you can change thi
|
||||||
**Arguments**:
|
**Arguments**:
|
||||||
|
|
||||||
* `view_name` - The view name that should be used as the target of the relationship. **required**.
|
* `view_name` - The view name that should be used as the target of the relationship. **required**.
|
||||||
|
* `view_namespace` - The namespace of the view, used as the target of the relationship. The default namespace can be set as HyperlinkedModelSerializerOptions attribute. If not set, it's `None`.
|
||||||
* `format` - If using format suffixes, hyperlinked fields will use the same format suffix for the target unless overridden by using the `format` argument.
|
* `format` - If using format suffixes, hyperlinked fields will use the same format suffix for the target unless overridden by using the `format` argument.
|
||||||
* `queryset` - By default `ModelSerializer` classes will use the default queryset for the relationship. `Serializer` classes must either set a queryset explicitly, or set `read_only=True`.
|
* `queryset` - By default `ModelSerializer` classes will use the default queryset for the relationship. `Serializer` classes must either set a queryset explicitly, or set `read_only=True`.
|
||||||
* `slug_field` - The field on the target that should be used for the lookup. Default is `'slug'`.
|
* `slug_field` - The field on the target that should be used for the lookup. Default is `'slug'`.
|
||||||
|
@ -329,6 +330,7 @@ This field is always read-only.
|
||||||
**Arguments**:
|
**Arguments**:
|
||||||
|
|
||||||
* `view_name` - The view name that should be used as the target of the relationship. **required**.
|
* `view_name` - The view name that should be used as the target of the relationship. **required**.
|
||||||
|
* `view_namespace` - The namespace of the view, used as the target of the relationship. The default namespace can be set as HyperlinkedModelSerializerOptions attribute. If not set, it's `None`.
|
||||||
* `format` - If using format suffixes, hyperlinked fields will use the same format suffix for the target unless overridden by using the `format` argument.
|
* `format` - If using format suffixes, hyperlinked fields will use the same format suffix for the target unless overridden by using the `format` argument.
|
||||||
* `slug_field` - The field on the target that should be used for the lookup. Default is `'slug'`.
|
* `slug_field` - The field on the target that should be used for the lookup. Default is `'slug'`.
|
||||||
* `pk_url_kwarg` - The named url parameter for the pk field lookup. Default is `pk`.
|
* `pk_url_kwarg` - The named url parameter for the pk field lookup. Default is `pk`.
|
||||||
|
|
|
@ -20,6 +20,12 @@ from rest_framework.compat import parse_date, parse_datetime
|
||||||
from rest_framework.compat import timezone
|
from rest_framework.compat import timezone
|
||||||
from urlparse import urlparse
|
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):
|
def is_simple_callable(obj):
|
||||||
"""
|
"""
|
||||||
|
@ -529,6 +535,8 @@ class HyperlinkedRelatedField(RelatedField):
|
||||||
self.view_name = kwargs.pop('view_name')
|
self.view_name = kwargs.pop('view_name')
|
||||||
except:
|
except:
|
||||||
raise ValueError("Hyperlinked field requires 'view_name' kwarg")
|
raise ValueError("Hyperlinked field requires 'view_name' kwarg")
|
||||||
|
|
||||||
|
self.view_namespace = kwargs.pop('view_namespace', Empty)
|
||||||
|
|
||||||
self.slug_field = kwargs.pop('slug_field', self.slug_field)
|
self.slug_field = kwargs.pop('slug_field', self.slug_field)
|
||||||
default_slug_kwarg = self.slug_url_kwarg or self.slug_field
|
default_slug_kwarg = self.slug_url_kwarg or self.slug_field
|
||||||
|
@ -538,6 +546,15 @@ class HyperlinkedRelatedField(RelatedField):
|
||||||
self.format = kwargs.pop('format', None)
|
self.format = kwargs.pop('format', None)
|
||||||
super(HyperlinkedRelatedField, self).__init__(*args, **kwargs)
|
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):
|
def get_slug_field(self):
|
||||||
"""
|
"""
|
||||||
Get the name of a slug field to be used to look up by slug.
|
Get the name of a slug field to be used to look up by slug.
|
||||||
|
@ -564,13 +581,13 @@ class HyperlinkedRelatedField(RelatedField):
|
||||||
|
|
||||||
kwargs = {self.slug_url_kwarg: slug}
|
kwargs = {self.slug_url_kwarg: slug}
|
||||||
try:
|
try:
|
||||||
return reverse(self.view_name, kwargs=kwargs, request=request, format=format)
|
return reverse(view_name, kwargs=kwargs, request=request, format=format)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
kwargs = {self.pk_url_kwarg: obj.pk, self.slug_url_kwarg: slug}
|
kwargs = {self.pk_url_kwarg: obj.pk, self.slug_url_kwarg: slug}
|
||||||
try:
|
try:
|
||||||
return reverse(self.view_name, kwargs=kwargs, request=request, format=format)
|
return reverse(view_name, kwargs=kwargs, request=request, format=format)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -634,9 +651,13 @@ class HyperlinkedIdentityField(Field):
|
||||||
slug_url_kwarg = None # Defaults to same as `slug_field` unless overridden
|
slug_url_kwarg = None # Defaults to same as `slug_field` unless overridden
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
# TODO: Make view_name mandatory, and have the
|
try:
|
||||||
# HyperlinkedModelSerializer set it on-the-fly
|
self.view_name = kwargs.pop('view_name')
|
||||||
self.view_name = kwargs.pop('view_name', None)
|
except:
|
||||||
|
raise ValueError("Hyperlinked Identity field requires 'view_name' kwarg")
|
||||||
|
|
||||||
|
self.view_namespace = kwargs.pop('view_namespace', Empty)
|
||||||
|
|
||||||
self.format = kwargs.pop('format', None)
|
self.format = kwargs.pop('format', None)
|
||||||
|
|
||||||
self.slug_field = kwargs.pop('slug_field', self.slug_field)
|
self.slug_field = kwargs.pop('slug_field', self.slug_field)
|
||||||
|
@ -646,10 +667,20 @@ class HyperlinkedIdentityField(Field):
|
||||||
|
|
||||||
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):
|
def field_to_native(self, obj, field_name):
|
||||||
request = self.context.get('request', None)
|
request = self.context.get('request', None)
|
||||||
format = self.format or self.context.get('format', None)
|
format = self.format or self.context.get('format', None)
|
||||||
view_name = self.view_name or self.parent.opts.view_name
|
view_name = self.view_name
|
||||||
kwargs = {self.pk_url_kwarg: obj.pk}
|
kwargs = {self.pk_url_kwarg: obj.pk}
|
||||||
try:
|
try:
|
||||||
return reverse(view_name, kwargs=kwargs, request=request, format=format)
|
return reverse(view_name, kwargs=kwargs, request=request, format=format)
|
||||||
|
@ -663,13 +694,13 @@ class HyperlinkedIdentityField(Field):
|
||||||
|
|
||||||
kwargs = {self.slug_url_kwarg: slug}
|
kwargs = {self.slug_url_kwarg: slug}
|
||||||
try:
|
try:
|
||||||
return reverse(self.view_name, kwargs=kwargs, request=request, format=format)
|
return reverse(view_name, kwargs=kwargs, request=request, format=format)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
kwargs = {self.pk_url_kwarg: obj.pk, self.slug_url_kwarg: slug}
|
kwargs = {self.pk_url_kwarg: obj.pk, self.slug_url_kwarg: slug}
|
||||||
try:
|
try:
|
||||||
return reverse(self.view_name, kwargs=kwargs, request=request, format=format)
|
return reverse(view_name, kwargs=kwargs, request=request, format=format)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ class GenericAPIView(views.APIView):
|
||||||
|
|
||||||
if serializer_class is None:
|
if serializer_class is None:
|
||||||
class DefaultSerializer(self.model_serializer_class):
|
class DefaultSerializer(self.model_serializer_class):
|
||||||
class Meta:
|
class Meta(self.model_serializer_class.Meta):
|
||||||
model = self.model
|
model = self.model
|
||||||
serializer_class = DefaultSerializer
|
serializer_class = DefaultSerializer
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ from decimal import Decimal
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.forms import widgets
|
from django.forms import widgets
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from rest_framework.compat import get_concrete_model
|
from rest_framework.compat import get_concrete_model
|
||||||
|
|
||||||
# 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:
|
||||||
|
@ -76,10 +77,34 @@ def _get_declared_fields(bases, attrs):
|
||||||
|
|
||||||
return SortedDict(fields)
|
return SortedDict(fields)
|
||||||
|
|
||||||
|
def _get_options_instance(bases, attrs):
|
||||||
|
options_class = Meta = None
|
||||||
|
if '_options_class' in attrs:
|
||||||
|
options_class = attrs['_options_class']
|
||||||
|
else:
|
||||||
|
for base in bases:
|
||||||
|
if hasattr(base, '_options_class'):
|
||||||
|
options_class = getattr(base, '_options_class')
|
||||||
|
break
|
||||||
|
if options_class is None:
|
||||||
|
raise ImproperlyConfigured, 'A Serializer requires an "_options_class" attribute'
|
||||||
|
|
||||||
|
if 'Meta' in attrs:
|
||||||
|
Meta = attrs['Meta']
|
||||||
|
else:
|
||||||
|
for base in bases:
|
||||||
|
if hasattr(base, 'Meta'):
|
||||||
|
Meta = getattr(base, 'Meta')
|
||||||
|
break
|
||||||
|
if Meta is None:
|
||||||
|
raise ImproperlyConfigured, 'A Serializer requires an "Meta" attribute'
|
||||||
|
|
||||||
|
return options_class(Meta)
|
||||||
|
|
||||||
class SerializerMetaclass(type):
|
class SerializerMetaclass(type):
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
attrs['base_fields'] = _get_declared_fields(bases, attrs)
|
attrs['base_fields'] = _get_declared_fields(bases, attrs)
|
||||||
|
attrs['opts'] = _get_options_instance(bases, attrs)
|
||||||
return super(SerializerMetaclass, cls).__new__(cls, name, bases, attrs)
|
return super(SerializerMetaclass, cls).__new__(cls, name, bases, attrs)
|
||||||
|
|
||||||
|
|
||||||
|
@ -102,7 +127,6 @@ class BaseSerializer(Field):
|
||||||
|
|
||||||
def __init__(self, instance=None, data=None, files=None, context=None, partial=False, **kwargs):
|
def __init__(self, instance=None, data=None, files=None, context=None, partial=False, **kwargs):
|
||||||
super(BaseSerializer, self).__init__(**kwargs)
|
super(BaseSerializer, self).__init__(**kwargs)
|
||||||
self.opts = self._options_class(self.Meta)
|
|
||||||
self.parent = None
|
self.parent = None
|
||||||
self.root = None
|
self.root = None
|
||||||
self.partial = partial
|
self.partial = partial
|
||||||
|
@ -512,6 +536,7 @@ class HyperlinkedModelSerializerOptions(ModelSerializerOptions):
|
||||||
def __init__(self, meta):
|
def __init__(self, meta):
|
||||||
super(HyperlinkedModelSerializerOptions, self).__init__(meta)
|
super(HyperlinkedModelSerializerOptions, self).__init__(meta)
|
||||||
self.view_name = getattr(meta, 'view_name', None)
|
self.view_name = getattr(meta, 'view_name', None)
|
||||||
|
self.view_namespace = getattr(meta, 'view_namespace', None)
|
||||||
|
|
||||||
|
|
||||||
class HyperlinkedModelSerializer(ModelSerializer):
|
class HyperlinkedModelSerializer(ModelSerializer):
|
||||||
|
@ -520,12 +545,14 @@ class HyperlinkedModelSerializer(ModelSerializer):
|
||||||
_options_class = HyperlinkedModelSerializerOptions
|
_options_class = HyperlinkedModelSerializerOptions
|
||||||
_default_view_name = '%(model_name)s-detail'
|
_default_view_name = '%(model_name)s-detail'
|
||||||
|
|
||||||
url = HyperlinkedIdentityField()
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(HyperlinkedModelSerializer, self).__init__(*args, **kwargs)
|
|
||||||
if self.opts.view_name is None:
|
if self.opts.view_name is None:
|
||||||
self.opts.view_name = self._get_default_view_name(self.opts.model)
|
self.opts.view_name = self._get_default_view_name(self.opts.model)
|
||||||
|
|
||||||
|
if not 'url' in self.base_fields:
|
||||||
|
self.base_fields.insert(0, 'url', HyperlinkedIdentityField(view_name=self.opts.view_name, view_namespace=self.opts.view_namespace))
|
||||||
|
|
||||||
|
super(HyperlinkedModelSerializer, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def _get_default_view_name(self, model):
|
def _get_default_view_name(self, model):
|
||||||
"""
|
"""
|
||||||
|
@ -551,7 +578,9 @@ class HyperlinkedModelSerializer(ModelSerializer):
|
||||||
queryset = rel._default_manager
|
queryset = rel._default_manager
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'queryset': queryset,
|
'queryset': queryset,
|
||||||
'view_name': self._get_default_view_name(rel)
|
'view_name': self._get_default_view_name(rel),
|
||||||
|
# TODO? offer possibility to init related fields with custom namespaces
|
||||||
|
'view_namespace': getattr(self.opts, 'view_namespace', None)
|
||||||
}
|
}
|
||||||
if to_many:
|
if to_many:
|
||||||
return ManyHyperlinkedRelatedField(**kwargs)
|
return ManyHyperlinkedRelatedField(**kwargs)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from django.conf.urls.defaults import patterns, url
|
from django.conf.urls.defaults import patterns, url, include
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.client import RequestFactory
|
from django.test.client import RequestFactory
|
||||||
from rest_framework import generics, status, serializers
|
from rest_framework import generics, status, serializers
|
||||||
|
@ -16,6 +16,16 @@ class BlogPostCommentSerializer(serializers.ModelSerializer):
|
||||||
model = BlogPostComment
|
model = BlogPostComment
|
||||||
fields = ('text', 'blog_post_url', 'url')
|
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):
|
class PhotoSerializer(serializers.Serializer):
|
||||||
description = serializers.CharField()
|
description = serializers.CharField()
|
||||||
|
@ -49,6 +59,13 @@ class ManyToManyDetail(generics.RetrieveAPIView):
|
||||||
model = ManyToManyModel
|
model = ManyToManyModel
|
||||||
model_serializer_class = serializers.HyperlinkedModelSerializer
|
model_serializer_class = serializers.HyperlinkedModelSerializer
|
||||||
|
|
||||||
|
class NamespacedManyToManySerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
view_namespace = 'namespacetests'
|
||||||
|
|
||||||
|
class NamespacedManyToManyDetail(generics.RetrieveAPIView):
|
||||||
|
model = ManyToManyModel
|
||||||
|
model_serializer_class = NamespacedManyToManySerializer
|
||||||
|
|
||||||
class BlogPostCommentListCreate(generics.ListCreateAPIView):
|
class BlogPostCommentListCreate(generics.ListCreateAPIView):
|
||||||
model = BlogPostComment
|
model = BlogPostComment
|
||||||
|
@ -58,6 +75,14 @@ class BlogPostCommentDetail(generics.RetrieveAPIView):
|
||||||
model = BlogPostComment
|
model = BlogPostComment
|
||||||
serializer_class = BlogPostCommentSerializer
|
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):
|
class BlogPostDetail(generics.RetrieveAPIView):
|
||||||
model = BlogPost
|
model = BlogPost
|
||||||
|
|
||||||
|
@ -76,7 +101,15 @@ class OptionalRelationDetail(generics.RetrieveAPIView):
|
||||||
model_serializer_class = serializers.HyperlinkedModelSerializer
|
model_serializer_class = serializers.HyperlinkedModelSerializer
|
||||||
|
|
||||||
|
|
||||||
|
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('',
|
urlpatterns = patterns('',
|
||||||
|
url(r'^other/namespace/', include(anchor_urls, namespace='namespacetests', app_name='namespacetests')),
|
||||||
url(r'^basic/$', BasicList.as_view(), name='basicmodel-list'),
|
url(r'^basic/$', BasicList.as_view(), name='basicmodel-list'),
|
||||||
url(r'^basic/(?P<pk>\d+)/$', BasicDetail.as_view(), name='basicmodel-detail'),
|
url(r'^basic/(?P<pk>\d+)/$', BasicDetail.as_view(), name='basicmodel-detail'),
|
||||||
url(r'^anchor/(?P<pk>\d+)/$', AnchorDetail.as_view(), name='anchor-detail'),
|
url(r'^anchor/(?P<pk>\d+)/$', AnchorDetail.as_view(), name='anchor-detail'),
|
||||||
|
@ -156,6 +189,15 @@ class TestManyToManyHyperlinkedView(TestCase):
|
||||||
}]
|
}]
|
||||||
self.list_view = ManyToManyList.as_view()
|
self.list_view = ManyToManyList.as_view()
|
||||||
self.detail_view = ManyToManyDetail.as_view()
|
self.detail_view = ManyToManyDetail.as_view()
|
||||||
|
self.namespaced_data = [{
|
||||||
|
'url': 'http://testserver/other/namespace/manytomany/1/',
|
||||||
|
'rel': [
|
||||||
|
'http://testserver/other/namespace/1/',
|
||||||
|
'http://testserver/other/namespace/2/',
|
||||||
|
'http://testserver/other/namespace/3/',
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
self.namespaced_detail_view = NamespacedManyToManyDetail.as_view()
|
||||||
|
|
||||||
def test_get_list_view(self):
|
def test_get_list_view(self):
|
||||||
"""
|
"""
|
||||||
|
@ -175,6 +217,15 @@ class TestManyToManyHyperlinkedView(TestCase):
|
||||||
self.assertEquals(response.status_code, status.HTTP_200_OK)
|
self.assertEquals(response.status_code, status.HTTP_200_OK)
|
||||||
self.assertEquals(response.data, self.data[0])
|
self.assertEquals(response.data, self.data[0])
|
||||||
|
|
||||||
|
def test_get_detail_namespaced_view(self):
|
||||||
|
"""
|
||||||
|
GET requests to a View in a namespace should succeed.
|
||||||
|
"""
|
||||||
|
request = factory.get('/other/namespace/manytomany/1/')
|
||||||
|
response = self.namespaced_detail_view(request, pk=1).render()
|
||||||
|
self.assertEquals(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEquals(response.data, self.namespaced_data[0])
|
||||||
|
|
||||||
|
|
||||||
class TestCreateWithForeignKeys(TestCase):
|
class TestCreateWithForeignKeys(TestCase):
|
||||||
urls = 'rest_framework.tests.hyperlinkedserializers'
|
urls = 'rest_framework.tests.hyperlinkedserializers'
|
||||||
|
@ -185,6 +236,7 @@ class TestCreateWithForeignKeys(TestCase):
|
||||||
"""
|
"""
|
||||||
self.post = BlogPost.objects.create(title="Test post")
|
self.post = BlogPost.objects.create(title="Test post")
|
||||||
self.create_view = BlogPostCommentListCreate.as_view()
|
self.create_view = BlogPostCommentListCreate.as_view()
|
||||||
|
self.create_namespaced_view = NamespacedBlogPostCommentListCreate.as_view()
|
||||||
|
|
||||||
def test_create_comment(self):
|
def test_create_comment(self):
|
||||||
|
|
||||||
|
@ -200,6 +252,26 @@ class TestCreateWithForeignKeys(TestCase):
|
||||||
self.assertEqual(self.post.blogpostcomment_set.count(), 1)
|
self.assertEqual(self.post.blogpostcomment_set.count(), 1)
|
||||||
self.assertEqual(self.post.blogpostcomment_set.all()[0].text, 'A test comment')
|
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):
|
class TestCreateWithForeignKeysAndCustomSlug(TestCase):
|
||||||
urls = 'rest_framework.tests.hyperlinkedserializers'
|
urls = 'rest_framework.tests.hyperlinkedserializers'
|
||||||
|
|
Loading…
Reference in New Issue
Block a user