Merge remote-tracking branch 'upstream/master'

* upstream/master: (54 commits)
  docs/renderers: `StaticHTMLRenderer` instead of `TemplateHTMLRenderer`
  Django 1.8 is no longer in beta
  Removed deprecated '.model' attribute check
  Update example of nested data
  Update serializers documentation (removed feature)
  more sp fixes
  Fix typo.
  Workaround for bug in pip
  Test against Django's master branch
  Django 1.8 released
  Should use "ordering" in model Meta, not order_by
  `query_params` in favor of `QUERY_PARAMS`
  Fixed docstring typo
  Reference typos in fields.md
  Use default reason phrases from HTTP standard.
  Metadata should detect null boolean field type.
  Fix older release notes link
  Try to resolve pep8 versioning clash.
  Swapping to hassattr logic for pk attribute references in relations
  Added enhancement for pk reference in many=True relations
  ...
This commit is contained in:
Rense VanderHoek 2015-04-18 13:44:31 +02:00
commit 5b7404cb9c
61 changed files with 1128 additions and 239 deletions

View File

@ -5,6 +5,10 @@ sudo: false
env: env:
- TOX_ENV=py27-flake8 - TOX_ENV=py27-flake8
- TOX_ENV=py27-docs - TOX_ENV=py27-docs
- TOX_ENV=py34-django18
- TOX_ENV=py33-django18
- TOX_ENV=py32-django18
- TOX_ENV=py27-django18
- TOX_ENV=py34-django17 - TOX_ENV=py34-django17
- TOX_ENV=py33-django17 - TOX_ENV=py33-django17
- TOX_ENV=py32-django17 - TOX_ENV=py32-django17
@ -21,10 +25,18 @@ env:
- TOX_ENV=py26-django15 - TOX_ENV=py26-django15
- TOX_ENV=py27-django14 - TOX_ENV=py27-django14
- TOX_ENV=py26-django14 - TOX_ENV=py26-django14
- TOX_ENV=py34-django18beta - TOX_ENV=py27-djangomaster
- TOX_ENV=py33-django18beta - TOX_ENV=py32-djangomaster
- TOX_ENV=py32-django18beta - TOX_ENV=py33-djangomaster
- TOX_ENV=py27-django18beta - TOX_ENV=py34-djangomaster
matrix:
fast_finish: true
allow_failures:
- env: TOX_ENV=py27-djangomaster
- env: TOX_ENV=py32-djangomaster
- env: TOX_ENV=py33-djangomaster
- env: TOX_ENV=py34-djangomaster
install: install:
- pip install tox - pip install tox

View File

@ -36,7 +36,7 @@ There is a live example API for testing purposes, [available here][sandbox].
# Requirements # Requirements
* Python (2.6.5+, 2.7, 3.2, 3.3, 3.4) * Python (2.6.5+, 2.7, 3.2, 3.3, 3.4)
* Django (1.4.11+, 1.5.6+, 1.6.3+, 1.7, 1.8-beta) * Django (1.4.11+, 1.5.6+, 1.6.3+, 1.7+, 1.8)
# Installation # Installation

View File

@ -434,7 +434,7 @@ A field class that does not take a value based on user input, but instead takes
For example, to include a field that always provides the current time as part of the serializer validated data, you would use the following: For example, to include a field that always provides the current time as part of the serializer validated data, you would use the following:
modified = serializer.HiddenField(default=timezone.now) modified = serializers.HiddenField(default=timezone.now)
The `HiddenField` class is usually only needed if you have some validation that needs to run based on some pre-provided field values, but you do not want to expose all of those fields to the end user. The `HiddenField` class is usually only needed if you have some validation that needs to run based on some pre-provided field values, but you do not want to expose all of those fields to the end user.
@ -481,7 +481,7 @@ If you want to create a custom field, you'll need to subclass `Field` and then o
The `.to_representation()` method is called to convert the initial datatype into a primitive, serializable datatype. The `.to_representation()` method is called to convert the initial datatype into a primitive, serializable datatype.
The `to_internal_value()` method is called to restore a primitive datatype into its internal python representation. This method should raise a `serializer.ValidationError` if the data is invalid. The `to_internal_value()` method is called to restore a primitive datatype into its internal python representation. This method should raise a `serializers.ValidationError` if the data is invalid.
Note that the `WritableField` class that was present in version 2.x no longer exists. You should subclass `Field` and override `to_internal_value()` if the field supports data input. Note that the `WritableField` class that was present in version 2.x no longer exists. You should subclass `Field` and override `to_internal_value()` if the field supports data input.

View File

@ -72,7 +72,7 @@ We can override `.get_queryset()` to deal with URLs such as `http://example.com/
by filtering against a `username` query parameter in the URL. by filtering against a `username` query parameter in the URL.
""" """
queryset = Purchase.objects.all() queryset = Purchase.objects.all()
username = self.request.QUERY_PARAMS.get('username', None) username = self.request.query_params.get('username', None)
if username is not None: if username is not None:
queryset = queryset.filter(purchaser__username=username) queryset = queryset.filter(purchaser__username=username)
return queryset return queryset

View File

@ -128,14 +128,14 @@ Note that if your API doesn't include any object level permissions, you may opti
Returns the classes that should be used to filter the queryset. Defaults to returning the `filter_backends` attribute. Returns the classes that should be used to filter the queryset. Defaults to returning the `filter_backends` attribute.
May be overridden to provide more complex behavior with filters, such as using different (or even exlusive) lists of filter_backends depending on different criteria. May be overridden to provide more complex behavior with filters, such as using different (or even exclusive) lists of filter_backends depending on different criteria.
For example: For example:
def get_filter_backends(self): def get_filter_backends(self):
if "geo_route" in self.request.QUERY_PARAMS: if "geo_route" in self.request.query_params:
return (GeoRouteFilter, CategoryFilter) return (GeoRouteFilter, CategoryFilter)
elif "geo_point" in self.request.QUERY_PARAMS: elif "geo_point" in self.request.query_params:
return (GeoPointFilter, CategoryFilter) return (GeoPointFilter, CategoryFilter)
return (CategoryFilter,) return (CategoryFilter,)

View File

@ -127,7 +127,7 @@ This pagination style mirrors the syntax used when looking up multiple database
#### Setup #### Setup
To enable the `PageNumberPagination` style globally, use the following configuration: To enable the `LimitOffsetPagination` style globally, use the following configuration:
REST_FRAMEWORK = { REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination' 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination'
@ -196,7 +196,7 @@ The `CursorPagination` class includes a number of attributes that may be overrid
To set these attributes you should override the `CursorPagination` class, and then enable your custom pagination class as above. To set these attributes you should override the `CursorPagination` class, and then enable your custom pagination class as above.
* `page_size` = A numeric value indicating the page size. If set, this overrides the `DEFAULT_PAGE_SIZE` setting. Defaults to the same value as the `DEFAULT_PAGE_SIZE` settings key. * `page_size` = A numeric value indicating the page size. If set, this overrides the `PAGE_SIZE` setting. Defaults to the same value as the `PAGE_SIZE` settings key.
* `cursor_query_param` = A string value indicating the name of the "cursor" query parameter. Defaults to `'cursor'`. * `cursor_query_param` = A string value indicating the name of the "cursor" query parameter. Defaults to `'cursor'`.
* `ordering` = This should be a string, or list of strings, indicating the field against which the cursor based pagination will be applied. For example: `ordering = 'slug'`. Defaults to `-created`. This value may also be overridden by using `OrderingFilter` on the view. * `ordering` = This should be a string, or list of strings, indicating the field against which the cursor based pagination will be applied. For example: `ordering = 'slug'`. Defaults to `-created`. This value may also be overridden by using `OrderingFilter` on the view.
* `template` = The name of a template to use when rendering pagination controls in the browsable API. May be overridden to modify the rendering style, or set to `None` to disable HTML pagination controls completely. Defaults to `"rest_framework/pagination/previous_and_next.html"`. * `template` = The name of a template to use when rendering pagination controls in the browsable API. May be overridden to modify the rendering style, or set to `None` to disable HTML pagination controls completely. Defaults to `"rest_framework/pagination/previous_and_next.html"`.

View File

@ -46,7 +46,7 @@ In order to explain the various types of relational fields, we'll use a couple o
class Meta: class Meta:
unique_together = ('album', 'order') unique_together = ('album', 'order')
order_by = 'order' ordering = ['order']
def __unicode__(self): def __unicode__(self):
return '%d: %s' % (self.order, self.title) return '%d: %s' % (self.order, self.title)

View File

@ -110,7 +110,7 @@ An example of a view that uses `TemplateHTMLRenderer`:
class UserDetail(generics.RetrieveAPIView): class UserDetail(generics.RetrieveAPIView):
""" """
A view that returns a templated HTML representations of a given user. A view that returns a templated HTML representation of a given user.
""" """
queryset = User.objects.all() queryset = User.objects.all()
renderer_classes = (TemplateHTMLRenderer,) renderer_classes = (TemplateHTMLRenderer,)
@ -135,7 +135,7 @@ See also: `StaticHTMLRenderer`
A simple renderer that simply returns pre-rendered HTML. Unlike other renderers, the data passed to the response object should be a string representing the content to be returned. A simple renderer that simply returns pre-rendered HTML. Unlike other renderers, the data passed to the response object should be a string representing the content to be returned.
An example of a view that uses `TemplateHTMLRenderer`: An example of a view that uses `StaticHTMLRenderer`:
@api_view(('GET',)) @api_view(('GET',))
@renderer_classes((StaticHTMLRenderer,)) @renderer_classes((StaticHTMLRenderer,))
@ -143,7 +143,7 @@ An example of a view that uses `TemplateHTMLRenderer`:
data = '<html><body><h1>Hello, world</h1></body></html>' data = '<html><body><h1>Hello, world</h1></body></html>'
return Response(data) return Response(data)
You can use `TemplateHTMLRenderer` either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint. You can use `StaticHTMLRenderer` either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint.
**.media_type**: `text/html` **.media_type**: `text/html`

View File

@ -344,7 +344,7 @@ Here's an example for an `update()` method on our previous `UserSerializer` clas
return instance return instance
Because the behavior of nested creates and updates can be ambiguous, and may require complex dependancies between related models, REST framework 3 requires you to always write these methods explicitly. The default `ModelSerializer` `.create()` and `.update()` methods do not include support for writable nested representations. Because the behavior of nested creates and updates can be ambiguous, and may require complex dependencies between related models, REST framework 3 requires you to always write these methods explicitly. The default `ModelSerializer` `.create()` and `.update()` methods do not include support for writable nested representations.
It is possible that a third party package, providing automatic support some kinds of automatic writable nested representations may be released alongside the 3.1 release. It is possible that a third party package, providing automatic support some kinds of automatic writable nested representations may be released alongside the 3.1 release.
@ -478,7 +478,7 @@ The default `ModelSerializer` uses primary keys for relationships, but you can a
The `depth` option should be set to an integer value that indicates the depth of relationships that should be traversed before reverting to a flat representation. The `depth` option should be set to an integer value that indicates the depth of relationships that should be traversed before reverting to a flat representation.
If you want to customize the way the serialization is done (e.g. using `allow_add_remove`) you'll need to define the field yourself. If you want to customize the way the serialization is done you'll need to define the field yourself.
## Specifying fields explicitly ## Specifying fields explicitly
@ -812,7 +812,7 @@ There are four methods that can be overridden, depending on what functionality y
* `.to_representation()` - Override this to support serialization, for read operations. * `.to_representation()` - Override this to support serialization, for read operations.
* `.to_internal_value()` - Override this to support deserialization, for write operations. * `.to_internal_value()` - Override this to support deserialization, for write operations.
* `.create()` and `.update()` - Overide either or both of these to support saving instances. * `.create()` and `.update()` - Override either or both of these to support saving instances.
Because this class provides the same interface as the `Serializer` class, you can use it with the existing generic class based views exactly as you would for a regular `Serializer` or `ModelSerializer`. Because this class provides the same interface as the `Serializer` class, you can use it with the existing generic class based views exactly as you would for a regular `Serializer` or `ModelSerializer`.

View File

@ -65,7 +65,7 @@ When testing views directly using a request factory, it's often convenient to be
To forcibly authenticate a request, use the `force_authenticate()` method. To forcibly authenticate a request, use the `force_authenticate()` method.
from rest_framework.tests import force_authenticate from rest_framework.test import force_authenticate
factory = APIRequestFactory() factory = APIRequestFactory()
user = User.objects.get(username='olivia') user = User.objects.get(username='olivia')

View File

@ -31,6 +31,8 @@ How you vary the API behavior is up to you, but one example you might typically
The `reverse` function included by REST framework ties in with the versioning scheme. You need to make sure to include the current `request` as a keyword argument, like so. The `reverse` function included by REST framework ties in with the versioning scheme. You need to make sure to include the current `request` as a keyword argument, like so.
from rest_framework.reverse import reverse
reverse('bookings-list', request=request) reverse('bookings-list', request=request)
The above function will apply any URL transformations appropriate to the request version. For example: The above function will apply any URL transformations appropriate to the request version. For example:
@ -103,7 +105,7 @@ Your client requests would now look like this:
Host: example.com Host: example.com
Accept: application/vnd.megacorp.bookings+json; version=1.0 Accept: application/vnd.megacorp.bookings+json; version=1.0
## URLParameterVersioning ## URLPathVersioning
This scheme requires the client to specify the version as part of the URL path. This scheme requires the client to specify the version as part of the URL path.

View File

@ -50,7 +50,7 @@ Some reasons you might want to use REST framework:
REST framework requires the following: REST framework requires the following:
* Python (2.6.5+, 2.7, 3.2, 3.3, 3.4) * Python (2.6.5+, 2.7, 3.2, 3.3, 3.4)
* Django (1.4.11+, 1.5.6+, 1.6.3+, 1.7, 1.8-beta) * Django (1.4.11+, 1.5.6+, 1.6.3+, 1.7+, 1.8)
The following packages are optional: The following packages are optional:

View File

@ -206,4 +206,4 @@ This will either be made as a single 3.2 release, or split across two separate r
[pagination]: ../api-guide/pagination.md [pagination]: ../api-guide/pagination.md
[versioning]: ../api-guide/versioning.md [versioning]: ../api-guide/versioning.md
[internationalization]: internationalization.md [internationalization]: internationalization.md
[customizing-field-mappings]: ../api-guide/serializers.md/#customizing-field-mappings [customizing-field-mappings]: ../api-guide/serializers.md#customizing-field-mappings

View File

@ -38,7 +38,24 @@ You can determine your currently installed version using `pip freeze`:
--- ---
## 3.0.x series ## 3.1.x series
### 3.1.1
**Date**: [23rd March 2015][3.1.1-milestone].
* **Security fix**: Escape tab switching cookie name in browsable API.
* Display input forms in browsable API if `serializer_class` is used, even when `get_serializer` method does not exist on the view. ([#2743](gh2743))
* Use a password input for the AuthTokenSerializer. ([#2741](gh2741))
* Fix missing anchor closing tag after next button. ([#2691][gh2691])
* Fix `lookup_url_kwarg` handling in viewsets. ([#2685][gh2685], [#2591][gh2591])
* Fix problem with importing `rest_framework.views` in `apps.py` ([#2678][gh2678])
* LimitOffsetPagination raises `TypeError` if PAGE_SIZE not set ([#2667][gh2667], [#2700][gh2700])
* German translation for `min_value` field error message references `max_value`. ([#2645][gh2645])
* Remove `MergeDict`. ([#2640][gh2640])
* Support serializing unsaved models with related fields. ([#2637][gh2637], [#2641][gh2641])
* Allow blank/null on radio.html choices. ([#2631][gh2631])
### 3.1.0 ### 3.1.0
@ -46,6 +63,10 @@ You can determine your currently installed version using `pip freeze`:
For full details see the [3.1 release announcement](3.1-announcement.md). For full details see the [3.1 release announcement](3.1-announcement.md).
---
## 3.0.x series
### 3.0.5 ### 3.0.5
**Date**: [10th February 2015][3.0.5-milestone]. **Date**: [10th February 2015][3.0.5-milestone].
@ -142,7 +163,7 @@ For full details see the [3.0 release announcement](3.0-announcement.md).
--- ---
For older release notes, [please see the version 2.x documentation](old-release-notes). For older release notes, [please see the version 2.x documentation][old-release-notes].
[cite]: http://www.catb.org/~esr/writings/cathedral-bazaar/cathedral-bazaar/ar01s04.html [cite]: http://www.catb.org/~esr/writings/cathedral-bazaar/cathedral-bazaar/ar01s04.html
[deprecation-policy]: #deprecation-policy [deprecation-policy]: #deprecation-policy
@ -154,13 +175,15 @@ For older release notes, [please see the version 2.x documentation](old-release-
[2.1.0-notes]: https://groups.google.com/d/topic/django-rest-framework/Vv2M0CMY9bg/discussion [2.1.0-notes]: https://groups.google.com/d/topic/django-rest-framework/Vv2M0CMY9bg/discussion
[ticket-582]: https://github.com/tomchristie/django-rest-framework/issues/582 [ticket-582]: https://github.com/tomchristie/django-rest-framework/issues/582
[rfc-6266]: http://tools.ietf.org/html/rfc6266#section-4.3 [rfc-6266]: http://tools.ietf.org/html/rfc6266#section-4.3
[old-release-notes]: http://tomchristie.github.io/rest-framework-2-docs/topics/release-notes#24x-series [old-release-notes]: https://github.com/tomchristie/django-rest-framework/blob/version-2.4.x/docs/topics/release-notes.md
[3.0.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.1+Release%22 [3.0.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.1+Release%22
[3.0.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.2+Release%22 [3.0.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.2+Release%22
[3.0.3-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.3+Release%22 [3.0.3-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.3+Release%22
[3.0.4-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.4+Release%22 [3.0.4-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.4+Release%22
[3.0.5-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.5+Release%22 [3.0.5-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.5+Release%22
[3.1.0-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.1.0+Release%22
[3.1.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.1.1+Release%22
<!-- 3.0.1 --> <!-- 3.0.1 -->
[gh2013]: https://github.com/tomchristie/django-rest-framework/issues/2013 [gh2013]: https://github.com/tomchristie/django-rest-framework/issues/2013
@ -259,3 +282,17 @@ For older release notes, [please see the version 2.x documentation](old-release-
[gh2519]: https://github.com/tomchristie/django-rest-framework/issues/2519 [gh2519]: https://github.com/tomchristie/django-rest-framework/issues/2519
[gh2524]: https://github.com/tomchristie/django-rest-framework/issues/2524 [gh2524]: https://github.com/tomchristie/django-rest-framework/issues/2524
[gh2530]: https://github.com/tomchristie/django-rest-framework/issues/2530 [gh2530]: https://github.com/tomchristie/django-rest-framework/issues/2530
<!-- 3.1.1 -->
[gh2691]: https://github.com/tomchristie/django-rest-framework/issues/2691
[gh2685]: https://github.com/tomchristie/django-rest-framework/issues/2685
[gh2591]: https://github.com/tomchristie/django-rest-framework/issues/2591
[gh2678]: https://github.com/tomchristie/django-rest-framework/issues/2678
[gh2667]: https://github.com/tomchristie/django-rest-framework/issues/2667
[gh2700]: https://github.com/tomchristie/django-rest-framework/issues/2700
[gh2645]: https://github.com/tomchristie/django-rest-framework/issues/2645
[gh2640]: https://github.com/tomchristie/django-rest-framework/issues/2640
[gh2637]: https://github.com/tomchristie/django-rest-framework/issues/2637
[gh2641]: https://github.com/tomchristie/django-rest-framework/issues/2641
[gh2631]: https://github.com/tomchristie/django-rest-framework/issues/2631
[gh2741]: https://github.com/tomchristie/django-rest-framework/issues/2641
[gh2743]: https://github.com/tomchristie/django-rest-framework/issues/2643

View File

@ -28,11 +28,11 @@ Some example output from our serializer.
{ {
'title': 'Leaving party preperations', 'title': 'Leaving party preperations',
'items': { 'items': [
{'text': 'Compile playlist', 'is_completed': True}, {'text': 'Compile playlist', 'is_completed': True},
{'text': 'Send invites', 'is_completed': False}, {'text': 'Send invites', 'is_completed': False},
{'text': 'Clean house', 'is_completed': False} {'text': 'Clean house', 'is_completed': False}
} ]
} }
Let's take a look at updating our nested one-to-many data structure. Let's take a look at updating our nested one-to-many data structure.

View File

@ -124,7 +124,7 @@ The first part of the serializer class defines the fields that get serialized/de
A serializer class is very similar to a Django `Form` class, and includes similar validation flags on the various fields, such as `required`, `max_length` and `default`. A serializer class is very similar to a Django `Form` class, and includes similar validation flags on the various fields, such as `required`, `max_length` and `default`.
The field flags can also control how the serializer should be displayed in certain circumstances, such as when rendering to HTML. The `{'base_template': 'textarea.html'}` flag above is equivelent to using `widget=widgets.Textarea` on a Django `Form` class. This is particularly useful for controlling how the browsable API should be displayed, as we'll see later in the tutorial. The field flags can also control how the serializer should be displayed in certain circumstances, such as when rendering to HTML. The `{'base_template': 'textarea.html'}` flag above is equivalent to using `widget=widgets.Textarea` on a Django `Form` class. This is particularly useful for controlling how the browsable API should be displayed, as we'll see later in the tutorial.
We can actually also save ourselves some time by using the `ModelSerializer` class, as we'll see later, but for now we'll keep our serializer definition explicit. We can actually also save ourselves some time by using the `ModelSerializer` class, as we'll see later, but for now we'll keep our serializer definition explicit.

View File

@ -200,7 +200,7 @@ See the [browsable api][browsable-api] topic for more information about the brow
In [tutorial part 3][tut-3], we'll start using class based views, and see how generic views reduce the amount of code we need to write. In [tutorial part 3][tut-3], we'll start using class based views, and see how generic views reduce the amount of code we need to write.
[json-url]: http://example.com/api/items/4.json [json-url]: http://example.com/api/items/4/.json
[devserver]: http://127.0.0.1:8000/snippets/ [devserver]: http://127.0.0.1:8000/snippets/
[browsable-api]: ../topics/browsable-api.md [browsable-api]: ../topics/browsable-api.md
[tut-1]: 1-serialization.md [tut-1]: 1-serialization.md

View File

@ -104,9 +104,11 @@ If we're going to have a hyperlinked API, we need to make sure we name our URL p
* Our user serializer includes a field that refers to `'snippet-detail'`. * Our user serializer includes a field that refers to `'snippet-detail'`.
* Our snippet and user serializers include `'url'` fields that by default will refer to `'{model_name}-detail'`, which in this case will be `'snippet-detail'` and `'user-detail'`. * Our snippet and user serializers include `'url'` fields that by default will refer to `'{model_name}-detail'`, which in this case will be `'snippet-detail'` and `'user-detail'`.
After adding all those names into our URLconf, our final `snippets/urls.py` file should look something like this: After adding all those names into our URLconf, our final `snippets/urls.py` file should look like this:
from django.conf.urls import url, include from django.conf.urls import url, include
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views
# API endpoints # API endpoints
urlpatterns = format_suffix_patterns([ urlpatterns = format_suffix_patterns([

View File

@ -1,3 +1,3 @@
# PEP8 code linting, which we run on all commits. # PEP8 code linting, which we run on all commits.
flake8==2.3.0 flake8==2.4.0
pep8==1.6.2 pep8==1.5.7

View File

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

View File

@ -6,7 +6,7 @@ from rest_framework import exceptions, serializers
class AuthTokenSerializer(serializers.Serializer): class AuthTokenSerializer(serializers.Serializer):
username = serializers.CharField() username = serializers.CharField()
password = serializers.CharField() password = serializers.CharField(style={'input_type': 'password'})
def validate(self, attrs): def validate(self, attrs):
username = attrs.get('username') username = attrs.get('username')

View File

@ -11,9 +11,10 @@ class ObtainAuthToken(APIView):
permission_classes = () permission_classes = ()
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,) parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,) renderer_classes = (renderers.JSONRenderer,)
serializer_class = AuthTokenSerializer
def post(self, request): def post(self, request):
serializer = AuthTokenSerializer(data=request.data) serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user'] user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user) token, created = Token.objects.get_or_create(user=user)

View File

@ -339,7 +339,7 @@ class Field(object):
* Raise `ValidationError`, indicating invalid data. * Raise `ValidationError`, indicating invalid data.
* Raise `SkipField`, indicating that the field should be ignored. * Raise `SkipField`, indicating that the field should be ignored.
* Return (True, data), indicating an empty value that should be * Return (True, data), indicating an empty value that should be
returned without any furhter validation being applied. returned without any further validation being applied.
* Return (False, data), indicating a non-empty value, that should * Return (False, data), indicating a non-empty value, that should
have validation applied as normal. have validation applied as normal.
""" """

View File

@ -3,14 +3,15 @@
# This file is distributed under the same license as the PACKAGE package. # This file is distributed under the same license as the PACKAGE package.
# #
# Translators: # Translators:
# Fabian Büchler <fabian@buechler.io>, 2015
# Thomas Tanner, 2015 # Thomas Tanner, 2015
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Django REST framework\n" "Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n" "POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n" "PO-Revision-Date: 2015-03-07 19:50+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n" "Last-Translator: Fabian Büchler <fabian@buechler.io>\n"
"Language-Team: German (http://www.transifex.com/projects/p/django-rest-framework/language/de/)\n" "Language-Team: German (http://www.transifex.com/projects/p/django-rest-framework/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -80,7 +81,7 @@ msgstr "Methode \"{method}\" nicht erlaubt."
#: exceptions.py:109 #: exceptions.py:109
msgid "Could not satisfy the request Accept header." msgid "Could not satisfy the request Accept header."
msgstr "Kann den Accept header der Anfrage nicht erfüllen." msgstr "Kann die Accept Kopfzeile der Anfrage nicht erfüllen."
#: exceptions.py:121 #: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request." msgid "Unsupported media type \"{media_type}\" in request."
@ -101,7 +102,7 @@ msgstr "Dieses Feld darf nicht Null sein."
#: fields.py:487 fields.py:515 #: fields.py:487 fields.py:515
msgid "\"{input}\" is not a valid boolean." msgid "\"{input}\" is not a valid boolean."
msgstr "\"{input}\" ist kein gültiger Boole'scher Wert." msgstr "\"{input}\" ist kein gültiger Wahrheitswert."
#: fields.py:550 #: fields.py:550
msgid "This field may not be blank." msgid "This field may not be blank."
@ -117,7 +118,7 @@ msgstr "Stelle sicher, dass dieses Feld mindestens {min_length} Zeichen lang ist
#: fields.py:587 #: fields.py:587
msgid "Enter a valid email address." msgid "Enter a valid email address."
msgstr "Gebe eine gültige E-Mail Adresse an." msgstr "Gib eine gültige E-Mail Adresse an."
#: fields.py:604 #: fields.py:604
msgid "This value does not match the required pattern." msgid "This value does not match the required pattern."
@ -127,15 +128,15 @@ msgstr "Dieser Wert passt nicht zu dem erforderlichen Muster."
msgid "" msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or " "Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens." "hyphens."
msgstr "Gebe ein gültiges \"slug\" aus Buchstaben, Ziffern, Unterstrichen und Minuszeichen ein." msgstr "Gib ein gültiges \"slug\" aus Buchstaben, Ziffern, Unterstrichen und Minuszeichen ein."
#: fields.py:627 #: fields.py:627
msgid "Enter a valid URL." msgid "Enter a valid URL."
msgstr "Gebe eine gültige URL ein." msgstr "Gib eine gültige URL ein."
#: fields.py:638 #: fields.py:638
msgid "\"{value}\" is not a valid UUID." msgid "\"{value}\" is not a valid UUID."
msgstr "" msgstr "\"{value}\" ist keine gültige UUID."
#: fields.py:657 #: fields.py:657
msgid "A valid integer is required." msgid "A valid integer is required."
@ -147,7 +148,7 @@ msgstr "Stelle sicher, dass dieser Wert kleiner oder gleich {max_value} ist."
#: fields.py:659 fields.py:693 fields.py:726 #: fields.py:659 fields.py:693 fields.py:726
msgid "Ensure this value is greater than or equal to {min_value}." msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "Stelle sicher, dass dieser Wert größer oder gleich {max_value} ist." msgstr "Stelle sicher, dass dieser Wert größer oder gleich {min_value} ist."
#: fields.py:660 fields.py:694 fields.py:730 #: fields.py:660 fields.py:694 fields.py:730
msgid "String value too large." msgid "String value too large."
@ -174,11 +175,11 @@ msgstr "Stelle sicher, dass es nicht mehr als {max_whole_places} Stellen vor dem
#: fields.py:813 #: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "Datum- und Zeitangabe hat das falsche Format. Nutze stattdessen eines dieser Formate: {format}." msgstr "Datums- und Zeitangabe hat das falsche Format. Nutze stattdessen eines dieser Formate: {format}."
#: fields.py:814 #: fields.py:814
msgid "Expected a datetime but got a date." msgid "Expected a datetime but got a date."
msgstr "Erwarte eine Datum- und Zeitangabe, erhielt aber ein Datum." msgstr "Erwarte eine Datums- und Zeitangabe, erhielt aber ein Datum."
#: fields.py:878 #: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}." msgid "Date has wrong format. Use one of these formats instead: {format}."
@ -186,7 +187,7 @@ msgstr "Datum hat das falsche Format. Nutze stattdessen eines dieser Formate: {f
#: fields.py:879 #: fields.py:879
msgid "Expected a date but got a datetime." msgid "Expected a date but got a datetime."
msgstr "Erwarte ein Datum, erhielt aber eine Datum- und Zeitangabe." msgstr "Erwarte ein Datum, erhielt aber eine Datums- und Zeitangabe."
#: fields.py:936 #: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}." msgid "Time has wrong format. Use one of these formats instead: {format}."
@ -207,7 +208,7 @@ msgstr "Es wurde keine Datei übermittelt."
#: fields.py:1068 #: fields.py:1068
msgid "" msgid ""
"The submitted data was not a file. Check the encoding type on the form." "The submitted data was not a file. Check the encoding type on the form."
msgstr "Die übermittelten Daten sind keine Datei. Prüfe den Kodierungstyp im Formular." msgstr "Die übermittelten Daten stellen keine Datei dar. Prüfe den Kodierungstyp im Formular."
#: fields.py:1069 #: fields.py:1069
msgid "No filename could be determined." msgid "No filename could be determined."
@ -220,53 +221,53 @@ msgstr "Die übermittelte Datei ist leer."
#: fields.py:1071 #: fields.py:1071
msgid "" msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})." "Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "" msgstr "Stelle sicher, dass dieser Dateiname höchstens {max_length} Zeichen lang ist (er hat {length})."
#: fields.py:1113 #: fields.py:1113
msgid "" msgid ""
"Upload a valid image. The file you uploaded was either not an image or a " "Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image." "corrupted image."
msgstr "" msgstr "Lade ein gültiges Bild hoch. Die hochgeladene Datei ist entweder kein Bild oder ein beschädigtes Bild."
#: fields.py:1188 #: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"." msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "" msgstr "Erwarte ein Dictionary mit Elementen, erhielt aber den Typ \"{input_type}\"."
#: pagination.py:221 #: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}." msgid "Invalid page \"{page_number}\": {message}."
msgstr "" msgstr "Ungültige Seite \"{page_number}\": {message}."
#: pagination.py:442 #: pagination.py:442
msgid "Invalid cursor" msgid "Invalid cursor"
msgstr "" msgstr "Ungültiger Zeiger"
#: relations.py:133 #: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist." msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "" msgstr "Ungültiger pk \"{pk_value}\" - Object existiert nicht."
#: relations.py:134 #: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}." msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "" msgstr "Falscher Typ. Erwarte pk Wert, erhielt aber {data_type}."
#: relations.py:157 #: relations.py:157
msgid "Invalid hyperlink - No URL match." msgid "Invalid hyperlink - No URL match."
msgstr "" msgstr "Ungültiger Hyperlink - entspricht keiner URL."
#: relations.py:158 #: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match." msgid "Invalid hyperlink - Incorrect URL match."
msgstr "" msgstr "Ungültiger Hyperlink - URL stimmt nicht überein."
#: relations.py:159 #: relations.py:159
msgid "Invalid hyperlink - Object does not exist." msgid "Invalid hyperlink - Object does not exist."
msgstr "" msgstr "Ungültiger Hyperlink - Objekt existiert nicht."
#: relations.py:160 #: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}." msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "" msgstr "Falscher Typ. Erwarte URL Zeichenkette, erhielt aber {data_type}."
#: relations.py:295 #: relations.py:295
msgid "Object with {slug_name}={value} does not exist." msgid "Object with {slug_name}={value} does not exist."
msgstr "" msgstr "Objekt mit {slug_name}={value} existiert nicht."
#: relations.py:296 #: relations.py:296
msgid "Invalid value." msgid "Invalid value."
@ -278,39 +279,39 @@ msgstr "Ungültige Daten. Dictionary erwartet, aber {datatype} erhalten."
#: validators.py:22 #: validators.py:22
msgid "This field must be unique." msgid "This field must be unique."
msgstr "Dieses Feld muss eineindeutig sein." msgstr "Dieses Feld muss eindeutig sein."
#: validators.py:76 #: validators.py:76
msgid "The fields {field_names} must make a unique set." msgid "The fields {field_names} must make a unique set."
msgstr "" msgstr "Die Felder {field_names} müssen eine eindeutige Menge bilden."
#: validators.py:219 #: validators.py:219
msgid "This field must be unique for the \"{date_field}\" date." msgid "This field must be unique for the \"{date_field}\" date."
msgstr "" msgstr "Dieses Feld muss bezüglich des \"{date_field}\" Datums eindeutig sein."
#: validators.py:234 #: validators.py:234
msgid "This field must be unique for the \"{date_field}\" month." msgid "This field must be unique for the \"{date_field}\" month."
msgstr "" msgstr "Dieses Feld muss bezüglich des \"{date_field}\" Monats eindeutig sein."
#: validators.py:247 #: validators.py:247
msgid "This field must be unique for the \"{date_field}\" year." msgid "This field must be unique for the \"{date_field}\" year."
msgstr "" msgstr "Dieses Feld muss bezüglich des \"{date_field}\" Jahrs eindeutig sein."
#: versioning.py:39 #: versioning.py:39
msgid "Invalid version in \"Accept\" header." msgid "Invalid version in \"Accept\" header."
msgstr "" msgstr "Ungültige Version in der \"Accept\" Kopfzeile."
#: versioning.py:70 versioning.py:112 #: versioning.py:70 versioning.py:112
msgid "Invalid version in URL path." msgid "Invalid version in URL path."
msgstr "" msgstr "Ungültige Version im URL Pfad."
#: versioning.py:138 #: versioning.py:138
msgid "Invalid version in hostname." msgid "Invalid version in hostname."
msgstr "" msgstr "Ungültige Version im Hostname."
#: versioning.py:160 #: versioning.py:160
msgid "Invalid version in query parameter." msgid "Invalid version in query parameter."
msgstr "" msgstr "Ungültige Version im Anfrageparameter."
#: authtoken/serializers.py:20 #: authtoken/serializers.py:20
msgid "User account is disabled." msgid "User account is disabled."

View File

@ -4,15 +4,15 @@
# #
# Translators: # Translators:
# José Padilla <jpadilla@webapplicate.com>, 2015 # José Padilla <jpadilla@webapplicate.com>, 2015
# Miguel González <migonzalvar@activitycentral.com>, 2015 # Miguel González <migonzalvar@gmail.com>, 2015
# Sergio Infante <rsinfante@gmail.com>, 2015 # Sergio Infante <rsinfante@gmail.com>, 2015
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Django REST framework\n" "Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n" "POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n" "PO-Revision-Date: 2015-03-06 19:51+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n" "Last-Translator: Miguel González <migonzalvar@gmail.com>\n"
"Language-Team: Spanish (http://www.transifex.com/projects/p/django-rest-framework/language/es/)\n" "Language-Team: Spanish (http://www.transifex.com/projects/p/django-rest-framework/language/es/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -137,7 +137,7 @@ msgstr "Introduzca una URL válida."
#: fields.py:638 #: fields.py:638
msgid "\"{value}\" is not a valid UUID." msgid "\"{value}\" is not a valid UUID."
msgstr "" msgstr "\"{value}\" no es un UUID válido."
#: fields.py:657 #: fields.py:657
msgid "A valid integer is required." msgid "A valid integer is required."
@ -232,7 +232,7 @@ msgstr "Adjunte una imagen válida. El archivo adjunto o bien no es una imagen o
#: fields.py:1188 #: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"." msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "" msgstr "Se esperaba un diccionario de elementos en vez del tipo \"{input_type}\"."
#: pagination.py:221 #: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}." msgid "Invalid page \"{page_number}\": {message}."
@ -240,7 +240,7 @@ msgstr "Página \"{page_number}\" inválida: {message}."
#: pagination.py:442 #: pagination.py:442
msgid "Invalid cursor" msgid "Invalid cursor"
msgstr "" msgstr "Cursor inválido"
#: relations.py:133 #: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist." msgid "Invalid pk \"{pk_value}\" - object does not exist."

View File

@ -9,8 +9,8 @@ msgstr ""
"Project-Id-Version: Django REST framework\n" "Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n" "POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n" "PO-Revision-Date: 2015-02-27 16:24+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n" "Last-Translator: Tõnis Kärdi <tonis.kardi@gmail.com>\n"
"Language-Team: Estonian (http://www.transifex.com/projects/p/django-rest-framework/language/et/)\n" "Language-Team: Estonian (http://www.transifex.com/projects/p/django-rest-framework/language/et/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -20,31 +20,31 @@ msgstr ""
#: authentication.py:69 #: authentication.py:69
msgid "Invalid basic header. No credentials provided." msgid "Invalid basic header. No credentials provided."
msgstr "" msgstr "Sobimatu lihtpäis. Kasutajatunnus on esitamata."
#: authentication.py:72 #: authentication.py:72
msgid "Invalid basic header. Credentials string should not contain spaces." msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "" msgstr "Sobimatu lihtpäis. Kasutajatunnus ei tohi sisaldada tühikuid."
#: authentication.py:78 #: authentication.py:78
msgid "Invalid basic header. Credentials not correctly base64 encoded." msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "" msgstr "Sobimatu lihtpäis. Kasutajatunnus pole korrektselt base64-kodeeritud."
#: authentication.py:90 #: authentication.py:90
msgid "Invalid username/password." msgid "Invalid username/password."
msgstr "Vale kasutajatunnus/salasõna." msgstr "Sobimatu kasutajatunnus/salasõna."
#: authentication.py:156 #: authentication.py:156
msgid "Invalid token header. No credentials provided." msgid "Invalid token header. No credentials provided."
msgstr "" msgstr "Sobimatu lubakaardi päis. Kasutajatunnus on esitamata."
#: authentication.py:159 #: authentication.py:159
msgid "Invalid token header. Token string should not contain spaces." msgid "Invalid token header. Token string should not contain spaces."
msgstr "" msgstr "Sobimatu lubakaardi päis. Loa sõne ei tohi sisaldada tühikuid."
#: authentication.py:168 #: authentication.py:168
msgid "Invalid token." msgid "Invalid token."
msgstr "" msgstr "Sobimatu lubakaart."
#: authentication.py:171 #: authentication.py:171
msgid "User inactive or deleted." msgid "User inactive or deleted."
@ -52,43 +52,43 @@ msgstr "Kasutaja on inaktiivne või kustutatud."
#: exceptions.py:38 #: exceptions.py:38
msgid "A server error occurred." msgid "A server error occurred."
msgstr "" msgstr "Viga serveril."
#: exceptions.py:73 #: exceptions.py:73
msgid "Malformed request." msgid "Malformed request."
msgstr "" msgstr "Väändunud päring."
#: exceptions.py:78 #: exceptions.py:78
msgid "Incorrect authentication credentials." msgid "Incorrect authentication credentials."
msgstr "" msgstr "Ebakorrektne autentimistunnus."
#: exceptions.py:83 #: exceptions.py:83
msgid "Authentication credentials were not provided." msgid "Authentication credentials were not provided."
msgstr "" msgstr "Autentimistunnus on esitamata."
#: exceptions.py:88 #: exceptions.py:88
msgid "You do not have permission to perform this action." msgid "You do not have permission to perform this action."
msgstr "" msgstr "Teil puuduvad piisavad õigused selle tegevuse teostamiseks."
#: exceptions.py:93 #: exceptions.py:93
msgid "Not found." msgid "Not found."
msgstr "" msgstr "Ei leidnud."
#: exceptions.py:98 #: exceptions.py:98
msgid "Method \"{method}\" not allowed." msgid "Method \"{method}\" not allowed."
msgstr "" msgstr "Meetod \"{method}\" pole lubatud."
#: exceptions.py:109 #: exceptions.py:109
msgid "Could not satisfy the request Accept header." msgid "Could not satisfy the request Accept header."
msgstr "" msgstr "Päringu Accept-päist ei suutnud täita."
#: exceptions.py:121 #: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request." msgid "Unsupported media type \"{media_type}\" in request."
msgstr "" msgstr "Meedia tüüpi {media_type} päringus ei toetata."
#: exceptions.py:134 #: exceptions.py:134
msgid "Request was throttled." msgid "Request was throttled."
msgstr "" msgstr "Liiga palju päringuid."
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 #: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155 #: validators.py:155
@ -101,23 +101,23 @@ msgstr "Väli ei tohi olla tühi."
#: fields.py:487 fields.py:515 #: fields.py:487 fields.py:515
msgid "\"{input}\" is not a valid boolean." msgid "\"{input}\" is not a valid boolean."
msgstr "" msgstr "\"{input}\" pole kehtiv kahendarv."
#: fields.py:550 #: fields.py:550
msgid "This field may not be blank." msgid "This field may not be blank."
msgstr "" msgstr "See väli ei tohi olla tühi."
#: fields.py:551 fields.py:1324 #: fields.py:551 fields.py:1324
msgid "Ensure this field has no more than {max_length} characters." msgid "Ensure this field has no more than {max_length} characters."
msgstr "" msgstr "Veendu, et see väli poleks pikem kui {max_length} tähemärki."
#: fields.py:552 #: fields.py:552
msgid "Ensure this field has at least {min_length} characters." msgid "Ensure this field has at least {min_length} characters."
msgstr "" msgstr "Veendu, et see väli oleks vähemalt {min_length} tähemärki pikk."
#: fields.py:587 #: fields.py:587
msgid "Enter a valid email address." msgid "Enter a valid email address."
msgstr "Sisesta kehtiv e-posti aadress." msgstr "Sisestage kehtiv e-posti aadress."
#: fields.py:604 #: fields.py:604
msgid "This value does not match the required pattern." msgid "This value does not match the required pattern."
@ -127,27 +127,27 @@ msgstr "Väärtus ei ühti etteantud mustriga."
msgid "" msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or " "Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens." "hyphens."
msgstr "" msgstr "Sisestage kehtiv \"slug\", mis koosneks tähtedest, numbritest, ala- või sidekriipsudest."
#: fields.py:627 #: fields.py:627
msgid "Enter a valid URL." msgid "Enter a valid URL."
msgstr "Sisesta korrektne URL." msgstr "Sisestage korrektne URL."
#: fields.py:638 #: fields.py:638
msgid "\"{value}\" is not a valid UUID." msgid "\"{value}\" is not a valid UUID."
msgstr "" msgstr "\"{value}\" pole kehtiv UUID."
#: fields.py:657 #: fields.py:657
msgid "A valid integer is required." msgid "A valid integer is required."
msgstr "" msgstr "Sisendiks peab olema täisarv."
#: fields.py:658 fields.py:692 fields.py:725 #: fields.py:658 fields.py:692 fields.py:725
msgid "Ensure this value is less than or equal to {max_value}." msgid "Ensure this value is less than or equal to {max_value}."
msgstr "Veendu, et väärtus on väiksem kui või võrdne väärtusega {max_value}. " msgstr "Veenduge, et väärtus on väiksem kui või võrdne väärtusega {max_value}. "
#: fields.py:659 fields.py:693 fields.py:726 #: fields.py:659 fields.py:693 fields.py:726
msgid "Ensure this value is greater than or equal to {min_value}." msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "Veendu, et väärtus on suurem kui või võrdne väärtusega {min_value}." msgstr "Veenduge, et väärtus on suurem kui või võrdne väärtusega {min_value}."
#: fields.py:660 fields.py:694 fields.py:730 #: fields.py:660 fields.py:694 fields.py:730
msgid "String value too large." msgid "String value too large."
@ -155,166 +155,166 @@ msgstr "Sõne on liiga pikk."
#: fields.py:691 fields.py:724 #: fields.py:691 fields.py:724
msgid "A valid number is required." msgid "A valid number is required."
msgstr "" msgstr "Sisendiks peab olema arv."
#: fields.py:727 #: fields.py:727
msgid "Ensure that there are no more than {max_digits} digits in total." msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "Veendu, et kokku pole rohkem kui {max_digits}." msgstr "Veenduge, et kokku pole rohkem kui {max_digits} numbit."
#: fields.py:728 #: fields.py:728
msgid "" msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places." "Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "Veendu, et komakohti pole rohkem kui {max_decimal_places}. " msgstr "Veenduge, et komakohti pole rohkem kui {max_decimal_places}. "
#: fields.py:729 #: fields.py:729
msgid "" msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the " "Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point." "decimal point."
msgstr "" msgstr "Veenduge, et täiskohti poleks rohkem kui {max_whole_digits}."
#: fields.py:813 #: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "" msgstr "Valesti formaaditud kuupäev-kellaaeg. Kasutage mõnda neist: {format}."
#: fields.py:814 #: fields.py:814
msgid "Expected a datetime but got a date." msgid "Expected a datetime but got a date."
msgstr "" msgstr "Ootasin kuupäev-kellaaeg andmetüüpi, kuid sain hoopis kuupäeva."
#: fields.py:878 #: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}." msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "" msgstr "Valesti formaaditud kuupäev. Kasutage mõnda neist: {format}."
#: fields.py:879 #: fields.py:879
msgid "Expected a date but got a datetime." msgid "Expected a date but got a datetime."
msgstr "" msgstr "Ootasin kuupäeva andmetüüpi, kuid sain hoopis kuupäev-kellaaja."
#: fields.py:936 #: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}." msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "" msgstr "Valesti formaaditud kellaaeg. Kasutage mõnda neist: {format}."
#: fields.py:992 fields.py:1036 #: fields.py:992 fields.py:1036
msgid "\"{input}\" is not a valid choice." msgid "\"{input}\" is not a valid choice."
msgstr "" msgstr "\"{input}\" on sobimatu valik."
#: fields.py:1037 fields.py:1151 serializers.py:482 #: fields.py:1037 fields.py:1151 serializers.py:482
msgid "Expected a list of items but got type \"{input_type}\"." msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "" msgstr "Ootasin kirjete järjendit, kuid sain \"{input_type}\" - tüübi."
#: fields.py:1067 #: fields.py:1067
msgid "No file was submitted." msgid "No file was submitted."
msgstr "" msgstr "Ühtegi faili ei esitatud."
#: fields.py:1068 #: fields.py:1068
msgid "" msgid ""
"The submitted data was not a file. Check the encoding type on the form." "The submitted data was not a file. Check the encoding type on the form."
msgstr "" msgstr "Esitatud andmetes ei olnud faili. Kontrollige vormi kodeeringut."
#: fields.py:1069 #: fields.py:1069
msgid "No filename could be determined." msgid "No filename could be determined."
msgstr "" msgstr "Ei suutnud tuvastada failinime."
#: fields.py:1070 #: fields.py:1070
msgid "The submitted file is empty." msgid "The submitted file is empty."
msgstr "" msgstr "Esitatud fail oli tühi."
#: fields.py:1071 #: fields.py:1071
msgid "" msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})." "Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "" msgstr "Veenduge, et failinimi oleks maksimaalselt {max_length} tähemärki pikk (praegu on {length})."
#: fields.py:1113 #: fields.py:1113
msgid "" msgid ""
"Upload a valid image. The file you uploaded was either not an image or a " "Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image." "corrupted image."
msgstr "" msgstr "Laadige üles kehtiv pildifail. Üles laetud fail ei olnud pilt või oli see katki."
#: fields.py:1188 #: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"." msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "" msgstr "Ootasin kirjete sõnastikku, kuid sain \"{input_type}\"."
#: pagination.py:221 #: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}." msgid "Invalid page \"{page_number}\": {message}."
msgstr "" msgstr "Sobimatu lehekülg \"{page_number}\": {message}."
#: pagination.py:442 #: pagination.py:442
msgid "Invalid cursor" msgid "Invalid cursor"
msgstr "" msgstr "Sobimatu kursor."
#: relations.py:133 #: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist." msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "" msgstr "Sobimatu primaarvõti \"{pk_value}\" - objekti pole olemas."
#: relations.py:134 #: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}." msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "" msgstr "Sobimatu andmetüüp. Ootasin primaarvõtit, sain {data_type}."
#: relations.py:157 #: relations.py:157
msgid "Invalid hyperlink - No URL match." msgid "Invalid hyperlink - No URL match."
msgstr "" msgstr "Sobimatu hüperlink - ei leidnud URLi vastet."
#: relations.py:158 #: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match." msgid "Invalid hyperlink - Incorrect URL match."
msgstr "" msgstr "Sobimatu hüperlink - vale URLi vaste."
#: relations.py:159 #: relations.py:159
msgid "Invalid hyperlink - Object does not exist." msgid "Invalid hyperlink - Object does not exist."
msgstr "" msgstr "Sobimatu hüperlink - objekti ei eksisteeri."
#: relations.py:160 #: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}." msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "" msgstr "Sobimatu andmetüüp. Ootasin URLi sõne, sain {data_type}."
#: relations.py:295 #: relations.py:295
msgid "Object with {slug_name}={value} does not exist." msgid "Object with {slug_name}={value} does not exist."
msgstr "" msgstr "Objekti {slug_name}={value} ei eksisteeri."
#: relations.py:296 #: relations.py:296
msgid "Invalid value." msgid "Invalid value."
msgstr "" msgstr "Sobimatu väärtus."
#: serializers.py:299 #: serializers.py:299
msgid "Invalid data. Expected a dictionary, but got {datatype}." msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "" msgstr "Sobimatud andmed. Ootasin sõnastikku, kuid sain {datatype}."
#: validators.py:22 #: validators.py:22
msgid "This field must be unique." msgid "This field must be unique."
msgstr "" msgstr "Selle välja väärtus peab olema unikaalne."
#: validators.py:76 #: validators.py:76
msgid "The fields {field_names} must make a unique set." msgid "The fields {field_names} must make a unique set."
msgstr "" msgstr "Veerud {field_names} peavad moodustama unikaalse hulga."
#: validators.py:219 #: validators.py:219
msgid "This field must be unique for the \"{date_field}\" date." msgid "This field must be unique for the \"{date_field}\" date."
msgstr "" msgstr "Selle välja väärtus peab olema unikaalne veerus \"{date_field}\" märgitud kuupäeval."
#: validators.py:234 #: validators.py:234
msgid "This field must be unique for the \"{date_field}\" month." msgid "This field must be unique for the \"{date_field}\" month."
msgstr "" msgstr "Selle välja väärtus peab olema unikaalneveerus \"{date_field}\" märgitud kuul."
#: validators.py:247 #: validators.py:247
msgid "This field must be unique for the \"{date_field}\" year." msgid "This field must be unique for the \"{date_field}\" year."
msgstr "" msgstr "Selle välja väärtus peab olema unikaalneveerus \"{date_field}\" märgitud aastal."
#: versioning.py:39 #: versioning.py:39
msgid "Invalid version in \"Accept\" header." msgid "Invalid version in \"Accept\" header."
msgstr "" msgstr "Sobimatu versioon \"Accept\" päises."
#: versioning.py:70 versioning.py:112 #: versioning.py:70 versioning.py:112
msgid "Invalid version in URL path." msgid "Invalid version in URL path."
msgstr "" msgstr "Sobimatu versioon URLi rajas."
#: versioning.py:138 #: versioning.py:138
msgid "Invalid version in hostname." msgid "Invalid version in hostname."
msgstr "" msgstr "Sobimatu versioon hostinimes."
#: versioning.py:160 #: versioning.py:160
msgid "Invalid version in query parameter." msgid "Invalid version in query parameter."
msgstr "" msgstr "Sobimatu versioon päringu parameetris."
#: authtoken/serializers.py:20 #: authtoken/serializers.py:20
msgid "User account is disabled." msgid "User account is disabled."
msgstr "Kasutajakonto on suletud" msgstr "Kasutajakonto on suletud."
#: authtoken/serializers.py:23 #: authtoken/serializers.py:23
msgid "Unable to log in with provided credentials." msgid "Unable to log in with provided credentials."

View File

@ -5,13 +5,15 @@
# Translators: # Translators:
# Etienne Desgagné <etienne.desgagne@evimbec.ca>, 2015 # Etienne Desgagné <etienne.desgagne@evimbec.ca>, 2015
# Martin Maillard <martin.maillard@gmail.com>, 2015 # Martin Maillard <martin.maillard@gmail.com>, 2015
# Martin Maillard <martin.maillard@gmail.com>, 2015
# Xavier Ordoquy <xordoquy@linovia.com>, 2015
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Django REST framework\n" "Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n" "POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n" "PO-Revision-Date: 2015-03-19 22:23+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n" "Last-Translator: Xavier Ordoquy <xordoquy@linovia.com>\n"
"Language-Team: French (http://www.transifex.com/projects/p/django-rest-framework/language/fr/)\n" "Language-Team: French (http://www.transifex.com/projects/p/django-rest-framework/language/fr/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -73,7 +75,7 @@ msgstr "Vous n'avez pas la permission d'effectuer cette action."
#: exceptions.py:93 #: exceptions.py:93
msgid "Not found." msgid "Not found."
msgstr "" msgstr "Pas trouvé."
#: exceptions.py:98 #: exceptions.py:98
msgid "Method \"{method}\" not allowed." msgid "Method \"{method}\" not allowed."
@ -81,15 +83,15 @@ msgstr "Méthode \"{method}\" non autorisée."
#: exceptions.py:109 #: exceptions.py:109
msgid "Could not satisfy the request Accept header." msgid "Could not satisfy the request Accept header."
msgstr "" msgstr "L'en-tête « Accept » n'a pas pu être satisfaite."
#: exceptions.py:121 #: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request." msgid "Unsupported media type \"{media_type}\" in request."
msgstr "" msgstr "Type de média \"{media_type}\" non supporté."
#: exceptions.py:134 #: exceptions.py:134
msgid "Request was throttled." msgid "Request was throttled."
msgstr "" msgstr "Requête ralentie."
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 #: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155 #: validators.py:155
@ -136,11 +138,11 @@ msgstr "Saisissez une URL valide."
#: fields.py:638 #: fields.py:638
msgid "\"{value}\" is not a valid UUID." msgid "\"{value}\" is not a valid UUID."
msgstr "" msgstr "\"{value}\" n'est pas un UUID valide."
#: fields.py:657 #: fields.py:657
msgid "A valid integer is required." msgid "A valid integer is required."
msgstr "Saisissez un nombre entier valide." msgstr "Un nombre entier valide est requis."
#: fields.py:658 fields.py:692 fields.py:725 #: fields.py:658 fields.py:692 fields.py:725
msgid "Ensure this value is less than or equal to {max_value}." msgid "Ensure this value is less than or equal to {max_value}."
@ -175,23 +177,23 @@ msgstr "Assurez-vous qu'il n'y a pas plus de {max_whole_digits} chiffres avant l
#: fields.py:813 #: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "" msgstr "La date + heure n'a pas le bon format. Utilisez un des formats suivants : {format}."
#: fields.py:814 #: fields.py:814
msgid "Expected a datetime but got a date." msgid "Expected a datetime but got a date."
msgstr "" msgstr "Attendait une date + heure mais a reçu une date."
#: fields.py:878 #: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}." msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "" msgstr "La date n'a pas le bon format. Utilisez un des formats suivants : {format}."
#: fields.py:879 #: fields.py:879
msgid "Expected a date but got a datetime." msgid "Expected a date but got a datetime."
msgstr "" msgstr "Attendait une date mais a reçu une date + heure."
#: fields.py:936 #: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}." msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "" msgstr "L'heure n'a pas le bon format. Utilisez un des formats suivants : {format}."
#: fields.py:992 fields.py:1036 #: fields.py:992 fields.py:1036
msgid "\"{input}\" is not a valid choice." msgid "\"{input}\" is not a valid choice."
@ -199,7 +201,7 @@ msgstr "\"{input}\" n'est pas un choix valide."
#: fields.py:1037 fields.py:1151 serializers.py:482 #: fields.py:1037 fields.py:1151 serializers.py:482
msgid "Expected a list of items but got type \"{input_type}\"." msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "" msgstr "Attendait une liste d'éléments mais a reçu \"{input_type}\"."
#: fields.py:1067 #: fields.py:1067
msgid "No file was submitted." msgid "No file was submitted."
@ -231,7 +233,7 @@ msgstr "Transférez une image valide. Le fichier que vous avez transféré n'est
#: fields.py:1188 #: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"." msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "" msgstr "Attendait un dictionnaire d'éléments mais a reçu \"{input_type}\"."
#: pagination.py:221 #: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}." msgid "Invalid page \"{page_number}\": {message}."
@ -239,7 +241,7 @@ msgstr "Page \"{page_number}\" non valide : {message}."
#: pagination.py:442 #: pagination.py:442
msgid "Invalid cursor" msgid "Invalid cursor"
msgstr "" msgstr "Curseur non valide"
#: relations.py:133 #: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist." msgid "Invalid pk \"{pk_value}\" - object does not exist."
@ -247,23 +249,23 @@ msgstr "Clé primaire \"{pk_value}\" non valide - l'objet n'existe pas."
#: relations.py:134 #: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}." msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "" msgstr "Type incorrect. Attendait une clé primaire, a reçu {data_type}."
#: relations.py:157 #: relations.py:157
msgid "Invalid hyperlink - No URL match." msgid "Invalid hyperlink - No URL match."
msgstr "" msgstr "Lien non valide : pas d'URL correspondante."
#: relations.py:158 #: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match." msgid "Invalid hyperlink - Incorrect URL match."
msgstr "" msgstr "Lien non valide : URL correspondante incorrecte."
#: relations.py:159 #: relations.py:159
msgid "Invalid hyperlink - Object does not exist." msgid "Invalid hyperlink - Object does not exist."
msgstr "" msgstr "Lien non valide : l'objet n'existe pas."
#: relations.py:160 #: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}." msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "" msgstr "Type incorrect. Attendait une URL, a reçu {data_type}."
#: relations.py:295 #: relations.py:295
msgid "Object with {slug_name}={value} does not exist." msgid "Object with {slug_name}={value} does not exist."
@ -275,7 +277,7 @@ msgstr "Valeur non valide."
#: serializers.py:299 #: serializers.py:299
msgid "Invalid data. Expected a dictionary, but got {datatype}." msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "" msgstr "Donnée non valide. Attendait un dictionnaire, a reçu {datatype}."
#: validators.py:22 #: validators.py:22
msgid "This field must be unique." msgid "This field must be unique."

View File

@ -4,14 +4,15 @@
# #
# Translators: # Translators:
# Janusz Harkot <jh@blueice.pl>, 2015 # Janusz Harkot <jh@blueice.pl>, 2015
# piotrjakimiak <legolass71@gmail.com>, 2015
# Maciek Olko <maciej.olko@gmail.com>, 2015 # Maciek Olko <maciej.olko@gmail.com>, 2015
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Django REST framework\n" "Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n" "POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n" "PO-Revision-Date: 2015-03-04 17:03+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n" "Last-Translator: piotrjakimiak <legolass71@gmail.com>\n"
"Language-Team: Polish (http://www.transifex.com/projects/p/django-rest-framework/language/pl/)\n" "Language-Team: Polish (http://www.transifex.com/projects/p/django-rest-framework/language/pl/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -136,7 +137,7 @@ msgstr "Wprowadź poprawny adres URL."
#: fields.py:638 #: fields.py:638
msgid "\"{value}\" is not a valid UUID." msgid "\"{value}\" is not a valid UUID."
msgstr "" msgstr "\"{value}\" nie jest poprawnym UUID."
#: fields.py:657 #: fields.py:657
msgid "A valid integer is required." msgid "A valid integer is required."
@ -231,7 +232,7 @@ msgstr "Prześlij poprawny plik graficzny. Przesłany plik albo nie jest grafik
#: fields.py:1188 #: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"." msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "" msgstr "Oczekiwano słownika, ale otrzymano \"{input_type}\"."
#: pagination.py:221 #: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}." msgid "Invalid page \"{page_number}\": {message}."
@ -239,7 +240,7 @@ msgstr "Niepoprawna strona \"{page_number}\": {message}."
#: pagination.py:442 #: pagination.py:442
msgid "Invalid cursor" msgid "Invalid cursor"
msgstr "" msgstr "Niepoprawny wskaźnik"
#: relations.py:133 #: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist." msgid "Invalid pk \"{pk_value}\" - object does not exist."

Binary file not shown.

View File

@ -0,0 +1,324 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-02 10:46+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Portuguese (Portugal) (http://www.transifex.com/projects/p/django-rest-framework/language/pt_PT/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: pt_PT\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: authentication.py:69
msgid "Invalid basic header. No credentials provided."
msgstr ""
#: authentication.py:72
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr ""
#: authentication.py:78
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr ""
#: authentication.py:90
msgid "Invalid username/password."
msgstr ""
#: authentication.py:156
msgid "Invalid token header. No credentials provided."
msgstr ""
#: authentication.py:159
msgid "Invalid token header. Token string should not contain spaces."
msgstr ""
#: authentication.py:168
msgid "Invalid token."
msgstr ""
#: authentication.py:171
msgid "User inactive or deleted."
msgstr ""
#: exceptions.py:38
msgid "A server error occurred."
msgstr ""
#: exceptions.py:73
msgid "Malformed request."
msgstr ""
#: exceptions.py:78
msgid "Incorrect authentication credentials."
msgstr ""
#: exceptions.py:83
msgid "Authentication credentials were not provided."
msgstr ""
#: exceptions.py:88
msgid "You do not have permission to perform this action."
msgstr ""
#: exceptions.py:93
msgid "Not found."
msgstr ""
#: exceptions.py:98
msgid "Method \"{method}\" not allowed."
msgstr ""
#: exceptions.py:109
msgid "Could not satisfy the request Accept header."
msgstr ""
#: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request."
msgstr ""
#: exceptions.py:134
msgid "Request was throttled."
msgstr ""
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155
msgid "This field is required."
msgstr ""
#: fields.py:154
msgid "This field may not be null."
msgstr ""
#: fields.py:487 fields.py:515
msgid "\"{input}\" is not a valid boolean."
msgstr ""
#: fields.py:550
msgid "This field may not be blank."
msgstr ""
#: fields.py:551 fields.py:1324
msgid "Ensure this field has no more than {max_length} characters."
msgstr ""
#: fields.py:552
msgid "Ensure this field has at least {min_length} characters."
msgstr ""
#: fields.py:587
msgid "Enter a valid email address."
msgstr ""
#: fields.py:604
msgid "This value does not match the required pattern."
msgstr ""
#: fields.py:615
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr ""
#: fields.py:627
msgid "Enter a valid URL."
msgstr ""
#: fields.py:638
msgid "\"{value}\" is not a valid UUID."
msgstr ""
#: fields.py:657
msgid "A valid integer is required."
msgstr ""
#: fields.py:658 fields.py:692 fields.py:725
msgid "Ensure this value is less than or equal to {max_value}."
msgstr ""
#: fields.py:659 fields.py:693 fields.py:726
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr ""
#: fields.py:660 fields.py:694 fields.py:730
msgid "String value too large."
msgstr ""
#: fields.py:691 fields.py:724
msgid "A valid number is required."
msgstr ""
#: fields.py:727
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr ""
#: fields.py:728
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr ""
#: fields.py:729
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr ""
#: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr ""
#: fields.py:814
msgid "Expected a datetime but got a date."
msgstr ""
#: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr ""
#: fields.py:879
msgid "Expected a date but got a datetime."
msgstr ""
#: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr ""
#: fields.py:992 fields.py:1036
msgid "\"{input}\" is not a valid choice."
msgstr ""
#: fields.py:1037 fields.py:1151 serializers.py:482
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr ""
#: fields.py:1067
msgid "No file was submitted."
msgstr ""
#: fields.py:1068
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr ""
#: fields.py:1069
msgid "No filename could be determined."
msgstr ""
#: fields.py:1070
msgid "The submitted file is empty."
msgstr ""
#: fields.py:1071
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr ""
#: fields.py:1113
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr ""
#: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr ""
#: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}."
msgstr ""
#: pagination.py:442
msgid "Invalid cursor"
msgstr ""
#: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr ""
#: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr ""
#: relations.py:157
msgid "Invalid hyperlink - No URL match."
msgstr ""
#: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match."
msgstr ""
#: relations.py:159
msgid "Invalid hyperlink - Object does not exist."
msgstr ""
#: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr ""
#: relations.py:295
msgid "Object with {slug_name}={value} does not exist."
msgstr ""
#: relations.py:296
msgid "Invalid value."
msgstr ""
#: serializers.py:299
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr ""
#: validators.py:22
msgid "This field must be unique."
msgstr ""
#: validators.py:76
msgid "The fields {field_names} must make a unique set."
msgstr ""
#: validators.py:219
msgid "This field must be unique for the \"{date_field}\" date."
msgstr ""
#: validators.py:234
msgid "This field must be unique for the \"{date_field}\" month."
msgstr ""
#: validators.py:247
msgid "This field must be unique for the \"{date_field}\" year."
msgstr ""
#: versioning.py:39
msgid "Invalid version in \"Accept\" header."
msgstr ""
#: versioning.py:70 versioning.py:112
msgid "Invalid version in URL path."
msgstr ""
#: versioning.py:138
msgid "Invalid version in hostname."
msgstr ""
#: versioning.py:160
msgid "Invalid version in query parameter."
msgstr ""
#: authtoken/serializers.py:20
msgid "User account is disabled."
msgstr ""
#: authtoken/serializers.py:23
msgid "Unable to log in with provided credentials."
msgstr ""
#: authtoken/serializers.py:26
msgid "Must include \"username\" and \"password\"."
msgstr ""

View File

@ -3,14 +3,15 @@
# This file is distributed under the same license as the PACKAGE package. # This file is distributed under the same license as the PACKAGE package.
# #
# Translators: # Translators:
# Kirill Tarasenko, 2015
# Mikhail Dmitriev <mktums@gmail.com>, 2015 # Mikhail Dmitriev <mktums@gmail.com>, 2015
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Django REST framework\n" "Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n" "POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n" "PO-Revision-Date: 2015-02-23 10:40+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n" "Last-Translator: Kirill Tarasenko\n"
"Language-Team: Russian (http://www.transifex.com/projects/p/django-rest-framework/language/ru/)\n" "Language-Team: Russian (http://www.transifex.com/projects/p/django-rest-framework/language/ru/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -135,7 +136,7 @@ msgstr "Введите корректный URL."
#: fields.py:638 #: fields.py:638
msgid "\"{value}\" is not a valid UUID." msgid "\"{value}\" is not a valid UUID."
msgstr "" msgstr "\"{value}\" не является корректным UUID."
#: fields.py:657 #: fields.py:657
msgid "A valid integer is required." msgid "A valid integer is required."
@ -230,7 +231,7 @@ msgstr "Загрузите корректное изображение. Загр
#: fields.py:1188 #: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"." msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "" msgstr "Ожидался словарь со значениями, но был получен \"{input_type}\"."
#: pagination.py:221 #: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}." msgid "Invalid page \"{page_number}\": {message}."
@ -238,7 +239,7 @@ msgstr "Недопустимая страница \"{page_number}\": {message}."
#: pagination.py:442 #: pagination.py:442
msgid "Invalid cursor" msgid "Invalid cursor"
msgstr "" msgstr "Не корректный курсор"
#: relations.py:133 #: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist." msgid "Invalid pk \"{pk_value}\" - object does not exist."
@ -278,11 +279,11 @@ msgstr "Недопустимые данные. Ожидался dictionary, но
#: validators.py:22 #: validators.py:22
msgid "This field must be unique." msgid "This field must be unique."
msgstr "" msgstr "Это поле должно быть уникально."
#: validators.py:76 #: validators.py:76
msgid "The fields {field_names} must make a unique set." msgid "The fields {field_names} must make a unique set."
msgstr "" msgstr "Поля {field_names} должны производить массив с уникальными значениями."
#: validators.py:219 #: validators.py:219
msgid "This field must be unique for the \"{date_field}\" date." msgid "This field must be unique for the \"{date_field}\" date."

View File

@ -3,6 +3,7 @@
# This file is distributed under the same license as the PACKAGE package. # This file is distributed under the same license as the PACKAGE package.
# #
# Translators: # Translators:
# Emrah BİLBAY <emrahbilbay@gmail.com>, 2015
# Ertaç Paprat <epaprat@gmail.com>, 2015 # Ertaç Paprat <epaprat@gmail.com>, 2015
# Mesut Can Gürle <mesutcang@gmail.com>, 2015 # Mesut Can Gürle <mesutcang@gmail.com>, 2015
# Recep KIRMIZI <rkirmizi@gmail.com>, 2015 # Recep KIRMIZI <rkirmizi@gmail.com>, 2015
@ -12,8 +13,8 @@ msgstr ""
"Project-Id-Version: Django REST framework\n" "Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n" "POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n" "PO-Revision-Date: 2015-03-10 23:50+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n" "Last-Translator: Emrah BİLBAY <emrahbilbay@gmail.com>\n"
"Language-Team: Turkish (http://www.transifex.com/projects/p/django-rest-framework/language/tr/)\n" "Language-Team: Turkish (http://www.transifex.com/projects/p/django-rest-framework/language/tr/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -23,15 +24,15 @@ msgstr ""
#: authentication.py:69 #: authentication.py:69
msgid "Invalid basic header. No credentials provided." msgid "Invalid basic header. No credentials provided."
msgstr "" msgstr "Geçersiz yetkilendirme başlığı. Gerekli uygunluk kriterleri sağlanmamış."
#: authentication.py:72 #: authentication.py:72
msgid "Invalid basic header. Credentials string should not contain spaces." msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "" msgstr "Geçersiz yetkilendirme başlığı. Uygunluk kriterine ait veri boşluk karakteri içermemeli."
#: authentication.py:78 #: authentication.py:78
msgid "Invalid basic header. Credentials not correctly base64 encoded." msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "" msgstr "Geçersiz yetkilendirme başlığı. Uygunluk kriterleri base64 formatına uygun olarak kodlanmamış."
#: authentication.py:90 #: authentication.py:90
msgid "Invalid username/password." msgid "Invalid username/password."
@ -83,7 +84,7 @@ msgstr "\"{method}\" metoduna izin verilmiyor."
#: exceptions.py:109 #: exceptions.py:109
msgid "Could not satisfy the request Accept header." msgid "Could not satisfy the request Accept header."
msgstr "" msgstr "İsteğe ait Accept başlık bilgisi yanıt verilecek başlık bilgileri arasında değil."
#: exceptions.py:121 #: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request." msgid "Unsupported media type \"{media_type}\" in request."
@ -91,7 +92,7 @@ msgstr "İstekte desteklenmeyen medya tipi: \"{media_type}\"."
#: exceptions.py:134 #: exceptions.py:134
msgid "Request was throttled." msgid "Request was throttled."
msgstr "" msgstr "Üst üste çok fazla istek yapıldı."
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 #: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155 #: validators.py:155
@ -138,7 +139,7 @@ msgstr "Geçerli bir URL girin."
#: fields.py:638 #: fields.py:638
msgid "\"{value}\" is not a valid UUID." msgid "\"{value}\" is not a valid UUID."
msgstr "" msgstr "\"{value}\" geçerli bir UUID değil."
#: fields.py:657 #: fields.py:657
msgid "A valid integer is required." msgid "A valid integer is required."
@ -233,7 +234,7 @@ msgstr "Geçerli bir resim yükleyin. Yüklediğiniz dosya resim değil ya da bo
#: fields.py:1188 #: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"." msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "" msgstr "Sözlük tipi bir değişken beklenirken \"{input_type}\" tipi bir değişken alındı."
#: pagination.py:221 #: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}." msgid "Invalid page \"{page_number}\": {message}."
@ -241,7 +242,7 @@ msgstr "Geçersiz sayfa \"{page_number}\":{message}."
#: pagination.py:442 #: pagination.py:442
msgid "Invalid cursor" msgid "Invalid cursor"
msgstr "" msgstr "Sayfalandırma imleci geçersiz"
#: relations.py:133 #: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist." msgid "Invalid pk \"{pk_value}\" - object does not exist."

Binary file not shown.

View File

@ -0,0 +1,324 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-02 10:46+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Vietnamese (http://www.transifex.com/projects/p/django-rest-framework/language/vi/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: vi\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: authentication.py:69
msgid "Invalid basic header. No credentials provided."
msgstr ""
#: authentication.py:72
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr ""
#: authentication.py:78
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr ""
#: authentication.py:90
msgid "Invalid username/password."
msgstr ""
#: authentication.py:156
msgid "Invalid token header. No credentials provided."
msgstr ""
#: authentication.py:159
msgid "Invalid token header. Token string should not contain spaces."
msgstr ""
#: authentication.py:168
msgid "Invalid token."
msgstr ""
#: authentication.py:171
msgid "User inactive or deleted."
msgstr ""
#: exceptions.py:38
msgid "A server error occurred."
msgstr ""
#: exceptions.py:73
msgid "Malformed request."
msgstr ""
#: exceptions.py:78
msgid "Incorrect authentication credentials."
msgstr ""
#: exceptions.py:83
msgid "Authentication credentials were not provided."
msgstr ""
#: exceptions.py:88
msgid "You do not have permission to perform this action."
msgstr ""
#: exceptions.py:93
msgid "Not found."
msgstr ""
#: exceptions.py:98
msgid "Method \"{method}\" not allowed."
msgstr ""
#: exceptions.py:109
msgid "Could not satisfy the request Accept header."
msgstr ""
#: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request."
msgstr ""
#: exceptions.py:134
msgid "Request was throttled."
msgstr ""
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155
msgid "This field is required."
msgstr ""
#: fields.py:154
msgid "This field may not be null."
msgstr ""
#: fields.py:487 fields.py:515
msgid "\"{input}\" is not a valid boolean."
msgstr ""
#: fields.py:550
msgid "This field may not be blank."
msgstr ""
#: fields.py:551 fields.py:1324
msgid "Ensure this field has no more than {max_length} characters."
msgstr ""
#: fields.py:552
msgid "Ensure this field has at least {min_length} characters."
msgstr ""
#: fields.py:587
msgid "Enter a valid email address."
msgstr ""
#: fields.py:604
msgid "This value does not match the required pattern."
msgstr ""
#: fields.py:615
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr ""
#: fields.py:627
msgid "Enter a valid URL."
msgstr ""
#: fields.py:638
msgid "\"{value}\" is not a valid UUID."
msgstr ""
#: fields.py:657
msgid "A valid integer is required."
msgstr ""
#: fields.py:658 fields.py:692 fields.py:725
msgid "Ensure this value is less than or equal to {max_value}."
msgstr ""
#: fields.py:659 fields.py:693 fields.py:726
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr ""
#: fields.py:660 fields.py:694 fields.py:730
msgid "String value too large."
msgstr ""
#: fields.py:691 fields.py:724
msgid "A valid number is required."
msgstr ""
#: fields.py:727
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr ""
#: fields.py:728
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr ""
#: fields.py:729
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr ""
#: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr ""
#: fields.py:814
msgid "Expected a datetime but got a date."
msgstr ""
#: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr ""
#: fields.py:879
msgid "Expected a date but got a datetime."
msgstr ""
#: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr ""
#: fields.py:992 fields.py:1036
msgid "\"{input}\" is not a valid choice."
msgstr ""
#: fields.py:1037 fields.py:1151 serializers.py:482
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr ""
#: fields.py:1067
msgid "No file was submitted."
msgstr ""
#: fields.py:1068
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr ""
#: fields.py:1069
msgid "No filename could be determined."
msgstr ""
#: fields.py:1070
msgid "The submitted file is empty."
msgstr ""
#: fields.py:1071
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr ""
#: fields.py:1113
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr ""
#: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr ""
#: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}."
msgstr ""
#: pagination.py:442
msgid "Invalid cursor"
msgstr ""
#: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr ""
#: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr ""
#: relations.py:157
msgid "Invalid hyperlink - No URL match."
msgstr ""
#: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match."
msgstr ""
#: relations.py:159
msgid "Invalid hyperlink - Object does not exist."
msgstr ""
#: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr ""
#: relations.py:295
msgid "Object with {slug_name}={value} does not exist."
msgstr ""
#: relations.py:296
msgid "Invalid value."
msgstr ""
#: serializers.py:299
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr ""
#: validators.py:22
msgid "This field must be unique."
msgstr ""
#: validators.py:76
msgid "The fields {field_names} must make a unique set."
msgstr ""
#: validators.py:219
msgid "This field must be unique for the \"{date_field}\" date."
msgstr ""
#: validators.py:234
msgid "This field must be unique for the \"{date_field}\" month."
msgstr ""
#: validators.py:247
msgid "This field must be unique for the \"{date_field}\" year."
msgstr ""
#: versioning.py:39
msgid "Invalid version in \"Accept\" header."
msgstr ""
#: versioning.py:70 versioning.py:112
msgid "Invalid version in URL path."
msgstr ""
#: versioning.py:138
msgid "Invalid version in hostname."
msgstr ""
#: versioning.py:160
msgid "Invalid version in query parameter."
msgstr ""
#: authtoken/serializers.py:20
msgid "User account is disabled."
msgstr ""
#: authtoken/serializers.py:23
msgid "Unable to log in with provided credentials."
msgstr ""
#: authtoken/serializers.py:26
msgid "Must include \"username\" and \"password\"."
msgstr ""

View File

@ -36,6 +36,7 @@ class SimpleMetadata(BaseMetadata):
label_lookup = ClassLookupDict({ label_lookup = ClassLookupDict({
serializers.Field: 'field', serializers.Field: 'field',
serializers.BooleanField: 'boolean', serializers.BooleanField: 'boolean',
serializers.NullBooleanField: 'boolean',
serializers.CharField: 'string', serializers.CharField: 'string',
serializers.URLField: 'url', serializers.URLField: 'url',
serializers.EmailField: 'email', serializers.EmailField: 'email',

View File

@ -10,7 +10,7 @@ from django.core.paginator import InvalidPage, Paginator as DjangoPaginator
from django.template import Context, loader from django.template import Context, loader
from django.utils import six from django.utils import six
from django.utils.six.moves.urllib import parse as urlparse from django.utils.six.moves.urllib import parse as urlparse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy as _
from rest_framework.compat import OrderedDict from rest_framework.compat import OrderedDict
from rest_framework.exceptions import NotFound from rest_framework.exceptions import NotFound
from rest_framework.response import Response from rest_framework.response import Response
@ -388,6 +388,9 @@ class LimitOffsetPagination(BasePagination):
def paginate_queryset(self, queryset, request, view=None): def paginate_queryset(self, queryset, request, view=None):
self.limit = self.get_limit(request) self.limit = self.get_limit(request)
if self.limit is None:
return None
self.offset = self.get_offset(request) self.offset = self.get_offset(request)
self.count = _get_count(queryset) self.count = _get_count(queryset)
self.request = request self.request = request
@ -491,6 +494,9 @@ class CursorPagination(BasePagination):
template = 'rest_framework/pagination/previous_and_next.html' template = 'rest_framework/pagination/previous_and_next.html'
def paginate_queryset(self, queryset, request, view=None): def paginate_queryset(self, queryset, request, view=None):
if self.page_size is None:
return None
self.base_url = request.build_absolute_uri() self.base_url = request.build_absolute_uri()
self.ordering = self.get_ordering(request, queryset, view) self.ordering = self.get_ordering(request, queryset, view)

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
from django.http import Http404 from django.http import Http404
from rest_framework.compat import get_model_name from rest_framework.compat import get_model_name
SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS'] SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS')
class BasePermission(object): class BasePermission(object):
@ -77,7 +77,7 @@ class DjangoModelPermissions(BasePermission):
`add`/`change`/`delete` permissions on the model. `add`/`change`/`delete` permissions on the model.
This permission can only be applied against view classes that This permission can only be applied against view classes that
provide a `.model` or `.queryset` attribute. provide a `.queryset` attribute.
""" """
# Map methods into required permission codes. # Map methods into required permission codes.
@ -107,24 +107,19 @@ class DjangoModelPermissions(BasePermission):
return [perm % kwargs for perm in self.perms_map[method]] return [perm % kwargs for perm in self.perms_map[method]]
def has_permission(self, request, view): def has_permission(self, request, view):
# Note that `.model` attribute on views is deprecated, although we
# enforce the deprecation on the view `get_serializer_class()` and
# `get_queryset()` methods, rather than here.
model_cls = getattr(view, 'model', None)
queryset = getattr(view, 'queryset', None) queryset = getattr(view, 'queryset', None)
if model_cls is None and queryset is not None:
model_cls = queryset.model
# Workaround to ensure DjangoModelPermissions are not applied # Workaround to ensure DjangoModelPermissions are not applied
# to the root view when using DefaultRouter. # to the root view when using DefaultRouter.
if model_cls is None and getattr(view, '_ignore_model_permissions', False): if queryset is None and getattr(view, '_ignore_model_permissions', False):
return True return True
assert model_cls, ('Cannot apply DjangoModelPermissions on a view that' assert queryset, (
' does not have `.model` or `.queryset` property.') 'Cannot apply DjangoModelPermissions on a view that '
'does not have `.queryset` property.'
)
perms = self.get_required_permissions(request.method, model_cls) perms = self.get_required_permissions(request.method, queryset.model)
return ( return (
request.user and request.user and
@ -150,7 +145,7 @@ class DjangoObjectPermissions(DjangoModelPermissions):
`add`/`change`/`delete` permissions on the object using .has_perms. `add`/`change`/`delete` permissions on the object using .has_perms.
This permission can only be applied against view classes that This permission can only be applied against view classes that
provide a `.model` or `.queryset` attribute. provide a `.queryset` attribute.
""" """
perms_map = { perms_map = {
@ -171,21 +166,17 @@ class DjangoObjectPermissions(DjangoModelPermissions):
return [perm % kwargs for perm in self.perms_map[method]] return [perm % kwargs for perm in self.perms_map[method]]
def has_object_permission(self, request, view, obj): def has_object_permission(self, request, view, obj):
model_cls = getattr(view, 'model', None) model_cls = view.queryset.model
queryset = getattr(view, 'queryset', None) user = request.user
if model_cls is None and queryset is not None:
model_cls = queryset.model
perms = self.get_required_object_permissions(request.method, model_cls) perms = self.get_required_object_permissions(request.method, model_cls)
user = request.user
if not user.has_perms(perms, obj): if not user.has_perms(perms, obj):
# If the user does not have permissions we need to determine if # If the user does not have permissions we need to determine if
# they have read permissions to see 403, or not, and simply see # they have read permissions to see 403, or not, and simply see
# a 404 response. # a 404 response.
if request.method in ('GET', 'OPTIONS', 'HEAD'): if request.method in SAFE_METHODS:
# Read permissions already checked and failed, no need # Read permissions already checked and failed, no need
# to make another lookup. # to make another lookup.
raise Http404 raise Http404

View File

@ -196,7 +196,7 @@ class HyperlinkedRelatedField(RelatedField):
attributes are not configured to correctly match the URL conf. attributes are not configured to correctly match the URL conf.
""" """
# Unsaved objects will not yet have a valid URL. # Unsaved objects will not yet have a valid URL.
if obj.pk is None: if hasattr(obj, 'pk') and obj.pk is None:
return None return None
lookup_value = getattr(obj, self.lookup_field) lookup_value = getattr(obj, self.lookup_field)
@ -361,7 +361,7 @@ class ManyRelatedField(Field):
def get_attribute(self, instance): def get_attribute(self, instance):
# Can't have any relationships if not created # Can't have any relationships if not created
if not instance.pk: if hasattr(instance, 'pk') and instance.pk is None:
return [] return []
relationship = get_attribute(instance, self.source_attrs) relationship = get_attribute(instance, self.source_attrs)

View File

@ -421,6 +421,14 @@ class BrowsableAPIRenderer(BaseRenderer):
return False # Doesn't have permissions return False # Doesn't have permissions
return True return True
def _get_serializer(self, serializer_class, view_instance, request, *args, **kwargs):
kwargs['context'] = {
'request': request,
'format': self.format,
'view': view_instance
}
return serializer_class(*args, **kwargs)
def get_rendered_html_form(self, data, view, method, request): def get_rendered_html_form(self, data, view, method, request):
""" """
Return a string representing a rendered HTML form, possibly bound to Return a string representing a rendered HTML form, possibly bound to
@ -457,8 +465,11 @@ class BrowsableAPIRenderer(BaseRenderer):
if method in ('DELETE', 'OPTIONS'): if method in ('DELETE', 'OPTIONS'):
return True # Don't actually need to return a form return True # Don't actually need to return a form
has_serializer = getattr(view, 'get_serializer', None)
has_serializer_class = getattr(view, 'serializer_class', None)
if ( if (
not getattr(view, 'get_serializer', None) or (not has_serializer and not has_serializer_class) or
not any(is_form_media_type(parser.media_type) for parser in view.parser_classes) not any(is_form_media_type(parser.media_type) for parser in view.parser_classes)
): ):
return return
@ -466,10 +477,19 @@ class BrowsableAPIRenderer(BaseRenderer):
if existing_serializer is not None: if existing_serializer is not None:
serializer = existing_serializer serializer = existing_serializer
else: else:
if method in ('PUT', 'PATCH'): if has_serializer:
serializer = view.get_serializer(instance=instance, **kwargs) if method in ('PUT', 'PATCH'):
serializer = view.get_serializer(instance=instance, **kwargs)
else:
serializer = view.get_serializer(**kwargs)
else: else:
serializer = view.get_serializer(**kwargs) # at this point we must have a serializer_class
if method in ('PUT', 'PATCH'):
serializer = self._get_serializer(view.serializer_class, view,
request, instance=instance, **kwargs)
else:
serializer = self._get_serializer(view.serializer_class, view,
request, **kwargs)
if hasattr(serializer, 'initial_data'): if hasattr(serializer, 'initial_data'):
serializer.is_valid() serializer.is_valid()

View File

@ -5,7 +5,7 @@ it is initialized with unrendered data, instead of a pre-rendered string.
The appropriate renderer is called during Django's template response rendering. The appropriate renderer is called during Django's template response rendering.
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
from django.core.handlers.wsgi import STATUS_CODE_TEXT from django.utils.six.moves.http_client import responses
from django.template.response import SimpleTemplateResponse from django.template.response import SimpleTemplateResponse
from django.utils import six from django.utils import six
@ -77,7 +77,7 @@ class Response(SimpleTemplateResponse):
""" """
# TODO: Deprecate and use a template tag instead # TODO: Deprecate and use a template tag instead
# TODO: Status code text for RFC 6585 status codes # TODO: Status code text for RFC 6585 status codes
return STATUS_CODE_TEXT.get(self.status_code, '') return responses.get(self.status_code, '')
def __getstate__(self): def __getstate__(self):
""" """

View File

@ -3,6 +3,7 @@ Provide urlresolver functions that return fully qualified URLs or view names
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
from django.core.urlresolvers import reverse as django_reverse from django.core.urlresolvers import reverse as django_reverse
from django.core.urlresolvers import NoReverseMatch
from django.utils import six from django.utils import six
from django.utils.functional import lazy from django.utils.functional import lazy
@ -15,7 +16,13 @@ def reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra
""" """
scheme = getattr(request, 'versioning_scheme', None) scheme = getattr(request, 'versioning_scheme', None)
if scheme is not None: if scheme is not None:
return scheme.reverse(viewname, args, kwargs, request, format, **extra) try:
return scheme.reverse(viewname, args, kwargs, request, format, **extra)
except NoReverseMatch:
# In case the versioning scheme reversal fails, fallback to the
# default implementation
pass
return _reverse(viewname, args, kwargs, request, format, **extra) return _reverse(viewname, args, kwargs, request, format, **extra)

View File

@ -218,14 +218,15 @@ class SimpleRouter(BaseRouter):
https://github.com/alanjds/drf-nested-routers https://github.com/alanjds/drf-nested-routers
""" """
base_regex = '(?P<{lookup_prefix}{lookup_field}>{lookup_value})' base_regex = '(?P<{lookup_prefix}{lookup_url_kwarg}>{lookup_value})'
# Use `pk` as default field, unset set. Default regex should not # Use `pk` as default field, unset set. Default regex should not
# consume `.json` style suffixes and should break at '/' boundaries. # consume `.json` style suffixes and should break at '/' boundaries.
lookup_field = getattr(viewset, 'lookup_field', 'pk') lookup_field = getattr(viewset, 'lookup_field', 'pk')
lookup_url_kwarg = getattr(viewset, 'lookup_url_kwarg', None) or lookup_field
lookup_value = getattr(viewset, 'lookup_value_regex', '[^/.]+') lookup_value = getattr(viewset, 'lookup_value_regex', '[^/.]+')
return base_regex.format( return base_regex.format(
lookup_prefix=lookup_prefix, lookup_prefix=lookup_prefix,
lookup_field=lookup_field, lookup_url_kwarg=lookup_url_kwarg,
lookup_value=lookup_value lookup_value=lookup_value
) )

View File

@ -44,6 +44,10 @@ $('a[data-toggle="tab"]').click(function(){
var selectedTab = null; var selectedTab = null;
var selectedTabName = getCookie('tabstyle'); var selectedTabName = getCookie('tabstyle');
if (selectedTabName) {
selectedTabName = selectedTabName.replace(/[^a-z-]/g, '');
}
if (selectedTabName) { if (selectedTabName) {
selectedTab = $('.form-switcher a[name=' + selectedTabName + ']'); selectedTab = $('.form-switcher a[name=' + selectedTabName + ']');
} }

View File

@ -1,20 +1,37 @@
{% load i18n %}
{% trans "None" as none_choice %}
<div class="form-group"> <div class="form-group">
{% if field.label %} {% if field.label %}
<label class="col-sm-2 control-label {% if style.hide_label %}sr-only{% endif %}">{{ field.label }}</label> <label class="col-sm-2 control-label {% if style.hide_label %}sr-only{% endif %}">{{ field.label }}</label>
{% endif %} {% endif %}
<div class="col-sm-10"> <div class="col-sm-10">
{% if style.inline %} {% if style.inline %}
{% if field.allow_null or field.allow_blank %}
<label class="radio-inline">
<input type="radio" name="{{ field.name }}" value="" {% if not field.value %}checked{% endif %} />
{{ none_choice }}
</label>
{% endif %}
{% for key, text in field.choices.items %} {% for key, text in field.choices.items %}
<label class="radio-inline"> <label class="radio-inline">
<input type="radio" name="{{ field.name }}" value="{{ key }}" {% if key == field.value %}checked{% endif %}> <input type="radio" name="{{ field.name }}" value="{{ key }}" {% if key == field.value %}checked{% endif %} />
{{ text }} {{ text }}
</label> </label>
{% endfor %} {% endfor %}
{% else %} {% else %}
{% if field.allow_null or field.allow_blank %}
<div class="radio">
<label>
<input type="radio" name="{{ field.name }}" value="" {% if not field.value %}checked{% endif %} />
{{ none_choice }}
</label>
</div>
{% endif %}
{% for key, text in field.choices.items %} {% for key, text in field.choices.items %}
<div class="radio"> <div class="radio">
<label> <label>
<input type="radio" name="{{ field.name }}" value="{{ key }}" {% if key == field.value %}checked{% endif %}> <input type="radio" name="{{ field.name }}" value="{{ key }}" {% if key == field.value %}checked{% endif %} />
{{ text }} {{ text }}
</label> </label>
</div> </div>

View File

@ -1,7 +1,18 @@
{% load i18n %}
{% trans "None" as none_choice %}
<div class="form-group {% if field.errors %}has-error{% endif %}"> <div class="form-group {% if field.errors %}has-error{% endif %}">
{% if field.label %} {% if field.label %}
<label class="sr-only">{{ field.label }}</label> <label class="sr-only">{{ field.label }}</label>
{% endif %} {% endif %}
{% if field.allow_null or field.allow_blank %}
<div class="radio">
<label>
<input type="radio" name="{{ field.name }}" value="" {% if not field.value %}checked{% endif %}>
{{ none_choice }}
</label>
</div>
{% endif %}
{% for key, text in field.choices.items %} {% for key, text in field.choices.items %}
<div class="radio"> <div class="radio">
<label> <label>

View File

@ -7,6 +7,6 @@
{% if next_url %} {% if next_url %}
<li class="next"><a href="{{ next_url }}">Next &raquo;</a></li> <li class="next"><a href="{{ next_url }}">Next &raquo;</a></li>
{% else %} {% else %}
<li class="next disabled"><a href="#">Next &raquo;</li> <li class="next disabled"><a href="#">Next &raquo;</a></li>
{% endif %} {% endif %}
</ul> </ul>

View File

@ -1,9 +1,18 @@
{% load i18n %}
{% trans "None" as none_choice %}
<div class="form-group {% if field.errors %}has-error{% endif %}"> <div class="form-group {% if field.errors %}has-error{% endif %}">
{% if field.label %} {% if field.label %}
<label {% if style.hide_label %}class="sr-only"{% endif %}>{{ field.label }}</label> <label {% if style.hide_label %}class="sr-only"{% endif %}>{{ field.label }}</label>
{% endif %} {% endif %}
{% if style.inline %} {% if style.inline %}
<div> <div>
{% if field.allow_null or field.allow_blank %}
<label class="radio-inline">
<input type="radio" name="{{ field.name }}" value="" {% if not field.value %}checked{% endif %} />
{{ none_choice }}
</label>
{% endif %}
{% for key, text in field.choices.items %} {% for key, text in field.choices.items %}
<label class="radio-inline"> <label class="radio-inline">
<input type="radio" name="{{ field.name }}" value="{{ key }}" {% if key == field.value %}checked{% endif %}> <input type="radio" name="{{ field.name }}" value="{{ key }}" {% if key == field.value %}checked{% endif %}>
@ -12,6 +21,14 @@
{% endfor %} {% endfor %}
</div> </div>
{% else %} {% else %}
{% if field.allow_null or field.allow_blank %}
<div class="radio">
<label>
<input type="radio" name="{{ field.name }}" value="" {% if not field.value %}checked{% endif %} />
{{ none_choice }}
</label>
</div>
{% endif %}
{% for key, text in field.choices.items %} {% for key, text in field.choices.items %}
<div class="radio"> <div class="radio">
<label> <label>

View File

@ -18,7 +18,6 @@ def pytest_configure():
MIDDLEWARE_CLASSES=( MIDDLEWARE_CLASSES=(
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
), ),
@ -27,7 +26,6 @@ def pytest_configure():
'django.contrib.contenttypes', 'django.contrib.contenttypes',
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.sites', 'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'rest_framework', 'rest_framework',
@ -35,12 +33,7 @@ def pytest_configure():
'tests', 'tests',
), ),
PASSWORD_HASHERS=( PASSWORD_HASHERS=(
'django.contrib.auth.hashers.SHA1PasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.BCryptPasswordHasher',
'django.contrib.auth.hashers.MD5PasswordHasher', 'django.contrib.auth.hashers.MD5PasswordHasher',
'django.contrib.auth.hashers.CryptPasswordHasher',
), ),
) )

View File

@ -1,5 +1,5 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from rest_framework import exceptions, serializers, status, views, versioning from rest_framework import exceptions, metadata, serializers, status, views, versioning
from rest_framework.request import Request from rest_framework.request import Request
from rest_framework.renderers import BrowsableAPIRenderer from rest_framework.renderers import BrowsableAPIRenderer
from rest_framework.test import APIRequestFactory from rest_framework.test import APIRequestFactory
@ -207,3 +207,8 @@ class TestMetadata:
scheme = versioning.QueryParameterVersioning scheme = versioning.QueryParameterVersioning
view = ExampleView.as_view(versioning_class=scheme) view = ExampleView.as_view(versioning_class=scheme)
view(request=request) view(request=request)
def test_null_boolean_field_info_type(self):
options = metadata.SimpleMetadata()
field_info = options.get_field_info(serializers.NullBooleanField())
assert field_info['type'] == 'boolean'

View File

@ -1,5 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import patterns, url from django.conf.urls import patterns, url
from django.core.urlresolvers import NoReverseMatch
from django.test import TestCase from django.test import TestCase
from rest_framework.reverse import reverse from rest_framework.reverse import reverse
from rest_framework.test import APIRequestFactory from rest_framework.test import APIRequestFactory
@ -16,6 +17,18 @@ urlpatterns = patterns(
) )
class MockVersioningScheme(object):
def __init__(self, raise_error=False):
self.raise_error = raise_error
def reverse(self, *args, **kwargs):
if self.raise_error:
raise NoReverseMatch()
return 'http://scheme-reversed/view'
class ReverseTests(TestCase): class ReverseTests(TestCase):
""" """
Tests for fully qualified URLs when using `reverse`. Tests for fully qualified URLs when using `reverse`.
@ -26,3 +39,17 @@ class ReverseTests(TestCase):
request = factory.get('/view') request = factory.get('/view')
url = reverse('view', request=request) url = reverse('view', request=request)
self.assertEqual(url, 'http://testserver/view') self.assertEqual(url, 'http://testserver/view')
def test_reverse_with_versioning_scheme(self):
request = factory.get('/view')
request.versioning_scheme = MockVersioningScheme()
url = reverse('view', request=request)
self.assertEqual(url, 'http://scheme-reversed/view')
def test_reverse_with_versioning_scheme_fallback_to_default_on_error(self):
request = factory.get('/view')
request.versioning_scheme = MockVersioningScheme(raise_error=True)
url = reverse('view', request=request)
self.assertEqual(url, 'http://testserver/view')

View File

@ -32,6 +32,13 @@ class NoteViewSet(viewsets.ModelViewSet):
lookup_field = 'uuid' lookup_field = 'uuid'
class KWargedNoteViewSet(viewsets.ModelViewSet):
queryset = RouterTestModel.objects.all()
serializer_class = NoteSerializer
lookup_field = 'text__contains'
lookup_url_kwarg = 'text'
class MockViewSet(viewsets.ModelViewSet): class MockViewSet(viewsets.ModelViewSet):
queryset = None queryset = None
serializer_class = None serializer_class = None
@ -40,6 +47,9 @@ class MockViewSet(viewsets.ModelViewSet):
notes_router = SimpleRouter() notes_router = SimpleRouter()
notes_router.register(r'notes', NoteViewSet) notes_router.register(r'notes', NoteViewSet)
kwarged_notes_router = SimpleRouter()
kwarged_notes_router.register(r'notes', KWargedNoteViewSet)
namespaced_router = DefaultRouter() namespaced_router = DefaultRouter()
namespaced_router.register(r'example', MockViewSet, base_name='example') namespaced_router.register(r'example', MockViewSet, base_name='example')
@ -47,6 +57,7 @@ urlpatterns = [
url(r'^non-namespaced/', include(namespaced_router.urls)), 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')),
url(r'^example/', include(notes_router.urls)), url(r'^example/', include(notes_router.urls)),
url(r'^example2/', include(kwarged_notes_router.urls)),
] ]
@ -177,6 +188,33 @@ class TestLookupValueRegex(TestCase):
self.assertEqual(expected[idx], self.urls[idx].regex.pattern) self.assertEqual(expected[idx], self.urls[idx].regex.pattern)
class TestLookupUrlKwargs(TestCase):
"""
Ensure the router honors lookup_url_kwarg.
Setup a deep lookup_field, but map it to a simple URL kwarg.
"""
urls = 'tests.test_routers'
def setUp(self):
RouterTestModel.objects.create(uuid='123', text='foo bar')
def test_custom_lookup_url_kwarg_route(self):
detail_route = kwarged_notes_router.urls[-1]
detail_url_pattern = detail_route.regex.pattern
self.assertIn('^notes/(?P<text>', detail_url_pattern)
def test_retrieve_lookup_url_kwarg_detail_view(self):
response = self.client.get('/example2/notes/fo/')
self.assertEqual(
response.data,
{
"url": "http://testserver/example/notes/123/",
"uuid": "123", "text": "foo bar"
}
)
class TestTrailingSlashIncluded(TestCase): class TestTrailingSlashIncluded(TestCase):
def setUp(self): def setUp(self):
class NoteViewSet(viewsets.ModelViewSet): class NoteViewSet(viewsets.ModelViewSet):

View File

@ -7,6 +7,7 @@ from rest_framework.response import Response
from rest_framework.reverse import reverse from rest_framework.reverse import reverse
from rest_framework.test import APIRequestFactory, APITestCase from rest_framework.test import APIRequestFactory, APITestCase
from rest_framework.versioning import NamespaceVersioning from rest_framework.versioning import NamespaceVersioning
from rest_framework.relations import PKOnlyObject
import pytest import pytest
@ -234,7 +235,7 @@ class TestInvalidVersion:
class TestHyperlinkedRelatedField(UsingURLPatterns, APITestCase): class TestHyperlinkedRelatedField(UsingURLPatterns, APITestCase):
included = [ included = [
url(r'^namespaced/(?P<pk>\d+)/$', dummy_view, name='namespaced'), url(r'^namespaced/(?P<pk>\d+)/$', dummy_pk_view, name='namespaced'),
] ]
urlpatterns = [ urlpatterns = [
@ -262,3 +263,44 @@ class TestHyperlinkedRelatedField(UsingURLPatterns, APITestCase):
assert self.field.to_internal_value('/v1/namespaced/3/') == 'object 3' assert self.field.to_internal_value('/v1/namespaced/3/') == 'object 3'
with pytest.raises(serializers.ValidationError): with pytest.raises(serializers.ValidationError):
self.field.to_internal_value('/v2/namespaced/3/') self.field.to_internal_value('/v2/namespaced/3/')
class TestNamespaceVersioningHyperlinkedRelatedFieldScheme(UsingURLPatterns, APITestCase):
included = [
url(r'^namespaced/(?P<pk>\d+)/$', dummy_pk_view, name='namespaced'),
]
urlpatterns = [
url(r'^v1/', include(included, namespace='v1')),
url(r'^v2/', include(included, namespace='v2')),
url(r'^non-api/(?P<pk>\d+)/$', dummy_pk_view, name='non-api-view')
]
def _create_field(self, view_name, version):
request = factory.get("/")
request.versioning_scheme = NamespaceVersioning()
request.version = version
field = serializers.HyperlinkedRelatedField(
view_name=view_name,
read_only=True)
field._context = {'request': request}
return field
def test_api_url_is_properly_reversed_with_v1(self):
field = self._create_field('namespaced', 'v1')
assert field.to_representation(PKOnlyObject(3)) == 'http://testserver/v1/namespaced/3/'
def test_api_url_is_properly_reversed_with_v2(self):
field = self._create_field('namespaced', 'v2')
assert field.to_representation(PKOnlyObject(5)) == 'http://testserver/v2/namespaced/5/'
def test_non_api_url_is_properly_reversed_regardless_of_the_version(self):
"""
Regression test for #2711
"""
field = self._create_field('non-api-view', 'v1')
assert field.to_representation(PKOnlyObject(10)) == 'http://testserver/non-api/10/'
field = self._create_field('non-api-view', 'v2')
assert field.to_representation(PKOnlyObject(10)) == 'http://testserver/non-api/10/'

View File

@ -3,7 +3,7 @@ envlist =
py27-{flake8,docs}, py27-{flake8,docs},
{py26,py27}-django14, {py26,py27}-django14,
{py26,py27,py32,py33,py34}-django{15,16}, {py26,py27,py32,py33,py34}-django{15,16},
{py27,py32,py33,py34}-django{17,18beta} {py27,py32,py33,py34}-django{17,18,master}
[testenv] [testenv]
commands = ./runtests.py --fast commands = ./runtests.py --fast
@ -14,14 +14,15 @@ deps =
django15: Django==1.5.6 # Should track minimum supported django15: Django==1.5.6 # Should track minimum supported
django16: Django==1.6.3 # Should track minimum supported django16: Django==1.6.3 # Should track minimum supported
django17: Django==1.7.2 # Should track maximum supported django17: Django==1.7.2 # Should track maximum supported
django18beta: https://www.djangoproject.com/download/1.8b1/tarball/ django18: Django==1.8 # Should track maximum supported
djangomaster: https://github.com/django/django/archive/master.tar.gz
-rrequirements/requirements-testing.txt -rrequirements/requirements-testing.txt
-rrequirements/requirements-optionals.txt -rrequirements/requirements-optionals.txt
[testenv:py27-flake8] [testenv:py27-flake8]
deps = deps =
-rrequirements/requirements-testing.txt
-rrequirements/requirements-codestyle.txt -rrequirements/requirements-codestyle.txt
-rrequirements/requirements-testing.txt
commands = ./runtests.py --lintonly commands = ./runtests.py --lintonly
[testenv:py27-docs] [testenv:py27-docs]