Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Anna 2017-02-19 08:51:10 +01:00
commit 040b823cc0
25 changed files with 221 additions and 66 deletions

View File

@ -25,9 +25,10 @@ The initial aim is to provide a single full-time position on REST framework.
<a href="https://getsentry.com/welcome/"><img src="https://raw.githubusercontent.com/tomchristie/django-rest-framework/master/docs/img/premium/sentry-readme.png"/></a>
<a href="https://getstream.io/try-the-api/?utm_source=drf&utm_medium=banner&utm_campaign=drf"><img src="https://raw.githubusercontent.com/tomchristie/django-rest-framework/master/docs/img/premium/stream-readme.png"/></a>
<a href="https://hello.machinalis.co.uk/"><img src="https://raw.githubusercontent.com/tomchristie/django-rest-framework/master/docs/img/premium/machinalis-readme.png"/></a>
<a href="https://rollbar.com/"><img src="https://raw.githubusercontent.com/tomchristie/django-rest-framework/master/docs/img/premium/rollbar-readme.png"/></a>
</p>
*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), and [Machinalis](https://hello.machinalis.co.uk/).*
*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), [Machinalis](https://hello.machinalis.co.uk/), and [Rollbar](https://rollbar.com).*
---

View File

@ -303,8 +303,6 @@ Format strings may either be [Python strftime formats][strftime] which explicitl
When a value of `None` is used for the format `datetime` objects will be returned by `to_representation` and the final output representation will determined by the renderer class.
In the case of JSON this means the default datetime representation uses the [ECMA 262 date time string specification][ecma262]. This is a subset of ISO 8601 which uses millisecond precision, and includes the 'Z' suffix for the UTC timezone, for example: `2013-01-29T12:34:56.123Z`.
#### `auto_now` and `auto_now_add` model fields.
When using `ModelSerializer` or `HyperlinkedModelSerializer`, note that any model fields with `auto_now=True` or `auto_now_add=True` will use serializer fields that are `read_only=True` by default.
@ -434,9 +432,11 @@ Requires either the `Pillow` package or `PIL` package. The `Pillow` package is
A field class that validates a list of objects.
**Signature**: `ListField(child)`
**Signature**: `ListField(child, min_length=None, max_length=None)`
- `child` - A field instance that should be used for validating the objects in the list. If this argument is not provided then objects in the list will not be validated.
- `min_length` - Validates that the list contains no fewer than this number of elements.
- `max_length` - Validates that the list contains no more than this number of elements.
For example, to validate a list of integers you might use something like the following:

View File

@ -700,7 +700,7 @@ You can override a URL field view name and lookup field by using either, or both
model = Account
fields = ('account_url', 'account_name', 'users', 'created')
extra_kwargs = {
'url': {'view_name': 'accounts', 'lookup_field': 'account_name'}
'url': {'view_name': 'accounts', 'lookup_field': 'account_name'},
'users': {'lookup_field': 'username'}
}
@ -1100,8 +1100,6 @@ This API included the `.get_field()`, `.get_pk_field()` and other methods.
Because the serializers have been fundamentally redesigned with 3.0 this API no longer exists. You can still modify the fields that get created but you'll need to refer to the source code, and be aware that if the changes you make are against private bits of API then they may be subject to change.
A new interface for controlling this behavior is currently planned for REST framework 3.1.
---
# Third party packages

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -75,10 +75,11 @@ The initial aim is to provide a single full-time position on REST framework.
<li><a href="https://getsentry.com/welcome/" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/sentry130.png)">Sentry</a></li>
<li><a href="https://getstream.io/try-the-api/?utm_source=drf&utm_medium=banner&utm_campaign=drf" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/stream-130.png)">Stream</a></li>
<li><a href="https://hello.machinalis.co.uk/" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/Machinalis130.png)">Machinalis</a></li>
<li><a href="https://rollbar.com" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/rollbar.png)">Rollbar</a></li>
</ul>
<div style="clear: both; padding-bottom: 20px;"></div>
*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), and [Machinalis](https://hello.machinalis.co.uk/).*
*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), [Machinalis](https://hello.machinalis.co.uk/), and [Rollbar](https://rollbar.com).*
---

View File

@ -40,6 +40,24 @@ You can determine your currently installed version using `pip freeze`:
## 3.5.x series
### 3.5.4
**Date**: [10th February 2017][3.5.4-milestone]
* Add max_length and min_length arguments for ListField. ([#4877][gh4877])
* Add per-view custom exception handler support. ([#4753][gh4753])
* Support disabling of declared fields on serializer subclasses. ([#4764][gh4764])
* Support custom view names on `@list_route` and `@detail_route` endpoints. ([#4821][gh4821])
* Correct labels for fields in login template when custom user model is used. ([#4841][gh4841])
* Whitespace fixes for descriptions generated from docstrings. ([#4759][gh4759], [#4869][gh4869], [#4870][gh4870])
* Better error reporting when schemas are returned by views without a schema renderer. ([#4790][gh4790])
* Fix for returned response of `PUT` requests when `prefetch_related` is used. ([#4661][gh4661], [#4668][gh4668])
* Fix for breadcrumb view names. ([#4750][gh4750])
* Fix for RequestsClient ensuring fully qualified URLs. ([#4678][gh4678])
* Fix for incorrect behavior of writable-nested fields check in some cases. ([#4634][gh4634], [#4669][gh4669])
* Resolve Django deprecation warnings. ([#4712][gh4712])
* Various cleanup of test cases.
### 3.5.3
**Date**: [7th November 2016][3.5.3-milestone]
@ -639,6 +657,7 @@ For older release notes, [please see the version 2.x documentation][old-release-
[3.5.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.5.1+Release%22
[3.5.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.5.2+Release%22
[3.5.3-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.5.3+Release%22
[3.5.4-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.5.4+Release%22
<!-- 3.0.1 -->
[gh2013]: https://github.com/tomchristie/django-rest-framework/issues/2013
@ -1216,3 +1235,22 @@ For older release notes, [please see the version 2.x documentation][old-release-
[gh4645]: https://github.com/tomchristie/django-rest-framework/issues/4645
[gh4646]: https://github.com/tomchristie/django-rest-framework/issues/4646
[gh4650]: https://github.com/tomchristie/django-rest-framework/issues/4650
<!-- 3.5.4 -->
[gh4877]: https://github.com/tomchristie/django-rest-framework/issues/4877
[gh4753]: https://github.com/tomchristie/django-rest-framework/issues/4753
[gh4764]: https://github.com/tomchristie/django-rest-framework/issues/4764
[gh4821]: https://github.com/tomchristie/django-rest-framework/issues/4821
[gh4841]: https://github.com/tomchristie/django-rest-framework/issues/4841
[gh4759]: https://github.com/tomchristie/django-rest-framework/issues/4759
[gh4869]: https://github.com/tomchristie/django-rest-framework/issues/4869
[gh4870]: https://github.com/tomchristie/django-rest-framework/issues/4870
[gh4790]: https://github.com/tomchristie/django-rest-framework/issues/4790
[gh4661]: https://github.com/tomchristie/django-rest-framework/issues/4661
[gh4668]: https://github.com/tomchristie/django-rest-framework/issues/4668
[gh4750]: https://github.com/tomchristie/django-rest-framework/issues/4750
[gh4678]: https://github.com/tomchristie/django-rest-framework/issues/4678
[gh4634]: https://github.com/tomchristie/django-rest-framework/issues/4634
[gh4669]: https://github.com/tomchristie/django-rest-framework/issues/4669
[gh4712]: https://github.com/tomchristie/django-rest-framework/issues/4712

View File

@ -8,7 +8,7 @@ ______ _____ _____ _____ __
"""
__title__ = 'Django REST framework'
__version__ = '3.5.3'
__version__ = '3.5.4'
__author__ = 'Tom Christie'
__license__ = 'BSD 2-Clause'
__copyright__ = 'Copyright 2011-2016 Tom Christie'

View File

@ -17,11 +17,6 @@ from django.template import Context, RequestContext, Template
from django.utils import six
from django.views.generic import View
try:
import importlib # Available in Python 3.1+
except ImportError:
from django.utils import importlib # Will be removed in Django 1.9
try:
from django.urls import (
@ -312,3 +307,10 @@ def set_many(instance, field, value):
else:
field = getattr(instance, field)
field.set(value)
def include(module, namespace=None, app_name=None):
from django.conf.urls import include
if django.VERSION < (1,9):
return include(module, namespace, app_name)
else:
return include((module, app_name), namespace)

View File

@ -1505,12 +1505,16 @@ class ListField(Field):
initial = []
default_error_messages = {
'not_a_list': _('Expected a list of items but got type "{input_type}".'),
'empty': _('This list may not be empty.')
'empty': _('This list may not be empty.'),
'min_length': _('Ensure this field has at least {min_length} elements.'),
'max_length': _('Ensure this field has no more than {max_length} elements.')
}
def __init__(self, *args, **kwargs):
self.child = kwargs.pop('child', copy.deepcopy(self.child))
self.allow_empty = kwargs.pop('allow_empty', True)
self.max_length = kwargs.pop('max_length', None)
self.min_length = kwargs.pop('min_length', None)
assert not inspect.isclass(self.child), '`child` has not been instantiated.'
assert self.child.source is None, (
@ -1520,6 +1524,12 @@ class ListField(Field):
super(ListField, self).__init__(*args, **kwargs)
self.child.bind(field_name='', parent=self)
if self.max_length is not None:
message = self.error_messages['max_length'].format(max_length=self.max_length)
self.validators.append(MaxLengthValidator(self.max_length, message=message))
if self.min_length is not None:
message = self.error_messages['min_length'].format(min_length=self.min_length)
self.validators.append(MinLengthValidator(self.min_length, message=message))
def get_value(self, dictionary):
if self.field_name not in dictionary:

View File

@ -18,13 +18,13 @@ REST framework settings, checking for user settings first, then falling
back to the defaults.
"""
from __future__ import unicode_literals
from importlib import import_module
from django.conf import settings
from django.test.signals import setting_changed
from django.utils import six
from rest_framework import ISO_8601
from rest_framework.compat import importlib
DEFAULTS = {
# Base API policies
@ -174,7 +174,7 @@ def import_from_string(val, setting_name):
# Nod to tastypie's use of importlib.
parts = val.split('.')
module_path, class_name = '.'.join(parts[:-1]), parts[-1]
module = importlib.import_module(module_path)
module = import_module(module_path)
return getattr(module, class_name)
except (ImportError, AttributeError) as e:
msg = "Could not import '%s' for API setting '%s'. %s: %s." % (val, setting_name, e.__class__.__name__, e)

View File

@ -114,7 +114,7 @@ if requests is not None:
self.mount('https://', adapter)
def request(self, method, url, *args, **kwargs):
if ':' not in url:
if not url.startswith('http'):
raise ValueError('Missing "http:" or "https:". Use a fully qualified URL, eg "http://testserver%s"' % url)
return super(RequestsClient, self).request(method, url, *args, **kwargs)

View File

@ -1,8 +1,8 @@
from __future__ import unicode_literals
from django.conf.urls import include, url
from django.conf.urls import url
from rest_framework.compat import RegexURLResolver
from rest_framework.compat import RegexURLResolver, include
from rest_framework.settings import api_settings

View File

@ -32,23 +32,18 @@ def dedent(content):
unindented text on the initial line.
"""
content = force_text(content)
whitespace_counts = [
len(line) - len(line.lstrip(' '))
for line in content.splitlines()[1:] if line.lstrip()
]
tab_counts = [
len(line) - len(line.lstrip('\t'))
for line in content.splitlines()[1:] if line.lstrip()
]
lines = [line for line in content.splitlines()[1:] if line.lstrip()]
# unindent the content if needed
if whitespace_counts:
whitespace_pattern = '^' + (' ' * min(whitespace_counts))
content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', content)
elif tab_counts:
whitespace_pattern = '^' + ('\t' * min(whitespace_counts))
content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', content)
if lines:
whitespace_counts = min([len(line) - len(line.lstrip(' ')) for line in lines])
tab_counts = min([len(line) - len(line.lstrip('\t')) for line in lines])
if whitespace_counts:
whitespace_pattern = '^' + (' ' * whitespace_counts)
content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', content)
elif tab_counts:
whitespace_pattern = '^' + ('\t' * tab_counts)
content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', content)
return content.strip()

View File

@ -76,12 +76,7 @@ def _get_forward_relationships(opts):
Returns an `OrderedDict` of field names to `RelationInfo`.
"""
forward_relations = OrderedDict()
for field in [
field for field in opts.fields
if field.serialize and get_remote_field(field) and not (field.primary_key and field.one_to_one)
# If the field is a OneToOneField and it's been marked as PK, then this
# is a multi-table inheritance auto created PK ('%_ptr').
]:
for field in [field for field in opts.fields if field.serialize and get_remote_field(field)]:
forward_relations[field.name] = RelationInfo(
model_field=field,
related_model=get_related_model(field),

View File

@ -124,4 +124,8 @@ class TestViewNamesAndDescriptions(TestCase):
def test_dedent_tabs():
assert dedent("\tfirst string\n\n\tsecond string") == 'first string\n\n\tsecond string'
result = 'first string\n\nsecond string'
assert dedent(" first string\n\n second string") == result
assert dedent("first string\n\n second string") == result
assert dedent("\tfirst string\n\n\tsecond string") == result
assert dedent("first string\n\n\tsecond string") == result

View File

@ -1,14 +1,20 @@
from datetime import date, datetime, timedelta, tzinfo
from datetime import date, datetime, timedelta
from decimal import Decimal
from uuid import uuid4
import pytest
from django.test import TestCase
from django.utils.timezone import utc
from rest_framework.compat import coreapi
from rest_framework.utils.encoders import JSONEncoder
class MockList(object):
def tolist(self):
return [1, 2, 3]
class JSONEncoderTests(TestCase):
"""
Tests the JSONEncoder method
@ -22,7 +28,7 @@ class JSONEncoderTests(TestCase):
Tests encoding a decimal
"""
d = Decimal(3.14)
assert d == float(d)
assert self.encoder.default(d) == float(d)
def test_encode_datetime(self):
"""
@ -30,6 +36,8 @@ class JSONEncoderTests(TestCase):
"""
current_time = datetime.now()
assert self.encoder.default(current_time) == current_time.isoformat()
current_time_utc = current_time.replace(tzinfo=utc)
assert self.encoder.default(current_time_utc) == current_time.isoformat() + 'Z'
def test_encode_time(self):
"""
@ -42,22 +50,8 @@ class JSONEncoderTests(TestCase):
"""
Tests encoding a timezone aware timestamp
"""
class UTC(tzinfo):
"""
Class extending tzinfo to mimic UTC time
"""
def utcoffset(self, dt):
return timedelta(0)
def tzname(self, dt):
return "UTC"
def dst(self, dt):
return timedelta(0)
current_time = datetime.now().time()
current_time = current_time.replace(tzinfo=UTC())
current_time = current_time.replace(tzinfo=utc)
with pytest.raises(ValueError):
self.encoder.default(current_time)
@ -91,3 +85,10 @@ class JSONEncoderTests(TestCase):
with pytest.raises(RuntimeError):
self.encoder.default(coreapi.Error())
def test_encode_object_with_tolist(self):
"""
Tests encoding a object with tolist method
"""
foo = MockList()
assert self.encoder.default(foo) == [1, 2, 3]

View File

@ -1668,6 +1668,16 @@ class TestEmptyListField(FieldValues):
field = serializers.ListField(child=serializers.IntegerField(), allow_empty=False)
class TestListFieldLengthLimit(FieldValues):
valid_inputs = ()
invalid_inputs = [
((0, 1), ['Ensure this field has at least 3 elements.']),
((0, 1, 2, 3, 4, 5), ['Ensure this field has no more than 4 elements.']),
]
outputs = ()
field = serializers.ListField(child=serializers.IntegerField(), min_length=3, max_length=4)
class TestUnvalidatedListField(FieldValues):
"""
Values for `ListField` with no `child` argument.

View File

@ -547,3 +547,94 @@ class TestGuardedQueryset(TestCase):
request = factory.get('/')
with pytest.raises(RuntimeError):
view(request).render()
class ApiViewsTests(TestCase):
def test_create_api_view_post(self):
class MockCreateApiView(generics.CreateAPIView):
def create(self, request, *args, **kwargs):
self.called = True
self.call_args = (request, args, kwargs)
view = MockCreateApiView()
data = ('test request', ('test arg',), {'test_kwarg': 'test'})
view.post('test request', 'test arg', test_kwarg='test')
assert view.called is True
assert view.call_args == data
def test_destroy_api_view_delete(self):
class MockDestroyApiView(generics.DestroyAPIView):
def destroy(self, request, *args, **kwargs):
self.called = True
self.call_args = (request, args, kwargs)
view = MockDestroyApiView()
data = ('test request', ('test arg',), {'test_kwarg': 'test'})
view.delete('test request', 'test arg', test_kwarg='test')
assert view.called is True
assert view.call_args == data
def test_update_api_view_partial_update(self):
class MockUpdateApiView(generics.UpdateAPIView):
def partial_update(self, request, *args, **kwargs):
self.called = True
self.call_args = (request, args, kwargs)
view = MockUpdateApiView()
data = ('test request', ('test arg',), {'test_kwarg': 'test'})
view.patch('test request', 'test arg', test_kwarg='test')
assert view.called is True
assert view.call_args == data
def test_retrieve_update_api_view_get(self):
class MockRetrieveUpdateApiView(generics.RetrieveUpdateAPIView):
def retrieve(self, request, *args, **kwargs):
self.called = True
self.call_args = (request, args, kwargs)
view = MockRetrieveUpdateApiView()
data = ('test request', ('test arg',), {'test_kwarg': 'test'})
view.get('test request', 'test arg', test_kwarg='test')
assert view.called is True
assert view.call_args == data
def test_retrieve_update_api_view_put(self):
class MockRetrieveUpdateApiView(generics.RetrieveUpdateAPIView):
def update(self, request, *args, **kwargs):
self.called = True
self.call_args = (request, args, kwargs)
view = MockRetrieveUpdateApiView()
data = ('test request', ('test arg',), {'test_kwarg': 'test'})
view.put('test request', 'test arg', test_kwarg='test')
assert view.called is True
assert view.call_args == data
def test_retrieve_update_api_view_patch(self):
class MockRetrieveUpdateApiView(generics.RetrieveUpdateAPIView):
def partial_update(self, request, *args, **kwargs):
self.called = True
self.call_args = (request, args, kwargs)
view = MockRetrieveUpdateApiView()
data = ('test request', ('test arg',), {'test_kwarg': 'test'})
view.patch('test request', 'test arg', test_kwarg='test')
assert view.called is True
assert view.call_args == data
def test_retrieve_destroy_api_view_get(self):
class MockRetrieveDestroyUApiView(generics.RetrieveDestroyAPIView):
def retrieve(self, request, *args, **kwargs):
self.called = True
self.call_args = (request, args, kwargs)
view = MockRetrieveDestroyUApiView()
data = ('test request', ('test arg',), {'test_kwarg': 'test'})
view.get('test request', 'test arg', test_kwarg='test')
assert view.called is True
assert view.call_args == data
def test_retrieve_destroy_api_view_delete(self):
class MockRetrieveDestroyUApiView(generics.RetrieveDestroyAPIView):
def destroy(self, request, *args, **kwargs):
self.called = True
self.call_args = (request, args, kwargs)
view = MockRetrieveDestroyUApiView()
data = ('test request', ('test arg',), {'test_kwarg': 'test'})
view.delete('test request', 'test arg', test_kwarg='test')
assert view.called is True
assert view.call_args == data

View File

@ -370,6 +370,13 @@ class TestLimitOffset:
assert self.pagination.display_page_controls
assert isinstance(self.pagination.to_html(), type(''))
def test_pagination_not_applied_if_limit_or_default_limit_not_set(self):
class MockPagination(pagination.LimitOffsetPagination):
default_limit = None
request = Request(factory.get('/'))
queryset = MockPagination().paginate_queryset(self.queryset, request)
assert queryset is None
def test_single_offset(self):
"""
When the offset is not a multiple of the limit we get some edge cases:

View File

@ -4,12 +4,13 @@ import json
from collections import namedtuple
import pytest
from django.conf.urls import include, url
from django.conf.urls import url
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.test import TestCase, override_settings
from rest_framework import permissions, serializers, viewsets
from rest_framework.compat import include
from rest_framework.decorators import detail_route, list_route
from rest_framework.response import Response
from rest_framework.routers import DefaultRouter, SimpleRouter
@ -81,7 +82,7 @@ empty_prefix_urls = [
urlpatterns = [
url(r'^non-namespaced/', include(namespaced_router.urls)),
url(r'^namespaced/', include(namespaced_router.urls, namespace='example')),
url(r'^namespaced/', include(namespaced_router.urls, namespace='example', app_name='example')),
url(r'^example/', include(notes_router.urls)),
url(r'^example2/', include(kwarged_notes_router.urls)),

View File

@ -1,8 +1,9 @@
import pytest
from django.conf.urls import include, url
from django.conf.urls import url
from django.test import override_settings
from rest_framework import serializers, status, versioning
from rest_framework.compat import include
from rest_framework.decorators import APIView
from rest_framework.relations import PKOnlyObject
from rest_framework.response import Response
@ -170,7 +171,7 @@ class TestURLReversing(URLPatternsTestCase):
]
urlpatterns = [
url(r'^v1/', include(included, namespace='v1')),
url(r'^v1/', include(included, namespace='v1', app_name='v1')),
url(r'^another/$', dummy_view, name='another'),
url(r'^(?P<version>[v1|v2]+)/another/$', dummy_view, name='another'),
]
@ -335,8 +336,8 @@ class TestHyperlinkedRelatedField(URLPatternsTestCase):
]
urlpatterns = [
url(r'^v1/', include(included, namespace='v1')),
url(r'^v2/', include(included, namespace='v2'))
url(r'^v1/', include(included, namespace='v1', app_name='v1')),
url(r'^v2/', include(included, namespace='v2', app_name='v2'))
]
def setUp(self):
@ -367,7 +368,7 @@ class TestNamespaceVersioningHyperlinkedRelatedFieldScheme(URLPatternsTestCase):
]
included = [
url(r'^namespaced/(?P<pk>\d+)/$', dummy_pk_view, name='namespaced'),
url(r'^nested/', include(nested, namespace='nested-namespace'))
url(r'^nested/', include(nested, namespace='nested-namespace', app_name='nested-namespace'))
]
urlpatterns = [