Commit 5b7404cb by Rense VanderHoek

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
  ...
parents b172ed0d ecb37f51
...@@ -5,6 +5,10 @@ sudo: false ...@@ -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: ...@@ -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
......
...@@ -36,7 +36,7 @@ There is a live example API for testing purposes, [available here][sandbox]. ...@@ -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
......
...@@ -434,7 +434,7 @@ A field class that does not take a value based on user input, but instead takes ...@@ -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 ...@@ -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.
......
...@@ -72,7 +72,7 @@ We can override `.get_queryset()` to deal with URLs such as `http://example.com/ ...@@ -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
......
...@@ -128,14 +128,14 @@ Note that if your API doesn't include any object level permissions, you may opti ...@@ -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,)
......
...@@ -127,7 +127,7 @@ This pagination style mirrors the syntax used when looking up multiple database ...@@ -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 ...@@ -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"`.
......
...@@ -46,7 +46,7 @@ In order to explain the various types of relational fields, we'll use a couple o ...@@ -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)
......
...@@ -110,7 +110,7 @@ An example of a view that uses `TemplateHTMLRenderer`: ...@@ -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` ...@@ -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`: ...@@ -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`
......
...@@ -344,7 +344,7 @@ Here's an example for an `update()` method on our previous `UserSerializer` clas ...@@ -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 ...@@ -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 ...@@ -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`.
......
...@@ -65,7 +65,7 @@ When testing views directly using a request factory, it's often convenient to be ...@@ -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')
......
...@@ -31,6 +31,8 @@ How you vary the API behavior is up to you, but one example you might typically ...@@ -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: ...@@ -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.
......
...@@ -50,7 +50,7 @@ Some reasons you might want to use REST framework: ...@@ -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:
......
...@@ -206,4 +206,4 @@ This will either be made as a single 3.2 release, or split across two separate r ...@@ -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
...@@ -38,7 +38,24 @@ You can determine your currently installed version using `pip freeze`: ...@@ -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`: ...@@ -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). ...@@ -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- ...@@ -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- ...@@ -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
...@@ -28,11 +28,11 @@ Some example output from our serializer. ...@@ -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.
......
...@@ -124,7 +124,7 @@ The first part of the serializer class defines the fields that get serialized/de ...@@ -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.
......
...@@ -200,7 +200,7 @@ See the [browsable api][browsable-api] topic for more information about the brow ...@@ -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
......
...@@ -104,9 +104,11 @@ If we're going to have a hyperlinked API, we need to make sure we name our URL p ...@@ -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([
......
# 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
...@@ -8,7 +8,7 @@ ______ _____ _____ _____ __ ...@@ -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'
......
...@@ -6,7 +6,7 @@ from rest_framework import exceptions, serializers ...@@ -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')
......
...@@ -11,9 +11,10 @@ class ObtainAuthToken(APIView): ...@@ -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)
......
...@@ -339,7 +339,7 @@ class Field(object): ...@@ -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.
""" """
......
...@@ -3,14 +3,15 @@ ...@@ -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." ...@@ -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." ...@@ -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 ...@@ -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." ...@@ -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." ...@@ -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 ...@@ -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 ...@@ -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." ...@@ -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." ...@@ -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." ...@@ -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."
......
...@@ -4,15 +4,15 @@ ...@@ -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." ...@@ -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 ...@@ -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}." ...@@ -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."
......
...@@ -5,13 +5,15 @@ ...@@ -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." ...@@ -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." ...@@ -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." ...@@ -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 ...@@ -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." ...@@ -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 ...@@ -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}." ...@@ -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." ...@@ -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." ...@@ -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."
......
...@@ -4,14 +4,15 @@ ...@@ -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." ...@@ -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 ...@@ -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}." ...@@ -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."
......
# 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 ""
...@@ -3,14 +3,15 @@ ...@@ -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." ...@@ -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 "Загрузите корректное изображение. Загр ...@@ -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}." ...@@ -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, но ...@@ -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."
......
...@@ -3,6 +3,7 @@ ...@@ -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 "" ...@@ -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 "" ...@@ -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." ...@@ -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}\"." ...@@ -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." ...@@ -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 ...@@ -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}." ...@@ -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."
......
# 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 ""
...@@ -36,6 +36,7 @@ class SimpleMetadata(BaseMetadata): ...@@ -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',
......
...@@ -10,7 +10,7 @@ from django.core.paginator import InvalidPage, Paginator as DjangoPaginator ...@@ -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): ...@@ -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): ...@@ -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)
......
...@@ -5,7 +5,7 @@ from __future__ import unicode_literals ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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
......
...@@ -196,7 +196,7 @@ class HyperlinkedRelatedField(RelatedField): ...@@ -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): ...@@ -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)
......
...@@ -421,6 +421,14 @@ class BrowsableAPIRenderer(BaseRenderer): ...@@ -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): ...@@ -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): ...@@ -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()
......
...@@ -5,7 +5,7 @@ it is initialized with unrendered data, instead of a pre-rendered string. ...@@ -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): ...@@ -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):
""" """
......
...@@ -3,6 +3,7 @@ Provide urlresolver functions that return fully qualified URLs or view names ...@@ -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 ...@@ -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)
......
...@@ -218,14 +218,15 @@ class SimpleRouter(BaseRouter): ...@@ -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
) )
......
...@@ -45,6 +45,10 @@ var selectedTab = null; ...@@ -45,6 +45,10 @@ var selectedTab = null;
var selectedTabName = getCookie('tabstyle'); var selectedTabName = getCookie('tabstyle');
if (selectedTabName) { if (selectedTabName) {
selectedTabName = selectedTabName.replace(/[^a-z-]/g, '');
}
if (selectedTabName) {
selectedTab = $('.form-switcher a[name=' + selectedTabName + ']'); selectedTab = $('.form-switcher a[name=' + selectedTabName + ']');
} }
......
{% 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>
......
{% 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>
......
...@@ -7,6 +7,6 @@ ...@@ -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>
{% 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 @@ ...@@ -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>
......
...@@ -18,7 +18,6 @@ def pytest_configure(): ...@@ -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(): ...@@ -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(): ...@@ -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',
), ),
) )
......
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: ...@@ -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'
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( ...@@ -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): ...@@ -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')
...@@ -32,6 +32,13 @@ class NoteViewSet(viewsets.ModelViewSet): ...@@ -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): ...@@ -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 = [ ...@@ -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): ...@@ -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):
......
...@@ -7,6 +7,7 @@ from rest_framework.response import Response ...@@ -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: ...@@ -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): ...@@ -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/'
...@@ -3,7 +3,7 @@ envlist = ...@@ -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 = ...@@ -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]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment