Commit eeff5a17 by Tom Christie

Merge branch 'master' into funding

parents 9aca6442 84e90134
...@@ -5,6 +5,9 @@ sudo: false ...@@ -5,6 +5,9 @@ sudo: false
env: env:
- TOX_ENV=py27-lint - TOX_ENV=py27-lint
- TOX_ENV=py27-docs - TOX_ENV=py27-docs
- TOX_ENV=py35-django19
- TOX_ENV=py34-django19
- TOX_ENV=py27-django19
- TOX_ENV=py34-django18 - TOX_ENV=py34-django18
- TOX_ENV=py33-django18 - TOX_ENV=py33-django18
- TOX_ENV=py32-django18 - TOX_ENV=py32-django18
...@@ -13,28 +16,12 @@ env: ...@@ -13,28 +16,12 @@ env:
- TOX_ENV=py33-django17 - TOX_ENV=py33-django17
- TOX_ENV=py32-django17 - TOX_ENV=py32-django17
- TOX_ENV=py27-django17 - TOX_ENV=py27-django17
- TOX_ENV=py34-django16
- TOX_ENV=py33-django16
- TOX_ENV=py32-django16
- TOX_ENV=py27-django16
- TOX_ENV=py26-django16
- TOX_ENV=py34-django15
- TOX_ENV=py33-django15
- TOX_ENV=py32-django15
- TOX_ENV=py27-django15
- TOX_ENV=py26-django15
- TOX_ENV=py27-djangomaster
- TOX_ENV=py32-djangomaster
- TOX_ENV=py33-djangomaster
- TOX_ENV=py34-djangomaster
matrix: matrix:
# Python 3.5 not yet available on travis, watch this to see when it is.
fast_finish: true fast_finish: true
allow_failures: allow_failures:
- env: TOX_ENV=py27-djangomaster - env: TOX_ENV=py35-django19
- env: TOX_ENV=py32-djangomaster
- env: TOX_ENV=py33-djangomaster
- env: TOX_ENV=py34-djangomaster
install: install:
- pip install tox - pip install tox
...@@ -44,4 +31,4 @@ script: ...@@ -44,4 +31,4 @@ script:
after_success: after_success:
- pip install codecov - pip install codecov
- codecov - codecov -e TOX_ENV
...@@ -36,8 +36,8 @@ There is a live example API for testing purposes, [available here][sandbox]. ...@@ -36,8 +36,8 @@ 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.7, 3.2, 3.3, 3.4, 3.5)
* Django (1.5.6+, 1.6.3+, 1.7, 1.8) * Django (1.7, 1.8, 1.9)
# Installation # Installation
...@@ -157,7 +157,6 @@ If you believe you’ve found something in Django REST framework which has secur ...@@ -157,7 +157,6 @@ If you believe you’ve found something in Django REST framework which has secur
Send a description of the issue via email to [rest-framework-security@googlegroups.com][security-mail]. The project maintainers will then work with you to resolve any issues where required, prior to any public disclosure. Send a description of the issue via email to [rest-framework-security@googlegroups.com][security-mail]. The project maintainers will then work with you to resolve any issues where required, prior to any public disclosure.
[build-status-image]: https://secure.travis-ci.org/tomchristie/django-rest-framework.svg?branch=master [build-status-image]: https://secure.travis-ci.org/tomchristie/django-rest-framework.svg?branch=master
[travis]: http://travis-ci.org/tomchristie/django-rest-framework?branch=master [travis]: http://travis-ci.org/tomchristie/django-rest-framework?branch=master
[coverage-status-image]: https://img.shields.io/codecov/c/github/tomchristie/django-rest-framework/master.svg [coverage-status-image]: https://img.shields.io/codecov/c/github/tomchristie/django-rest-framework/master.svg
......
...@@ -360,6 +360,14 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a ...@@ -360,6 +360,14 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a
[Django-rest-auth][django-rest-auth] library provides a set of REST API endpoints for registration, authentication (including social media authentication), password reset, retrieve and update user details, etc. By having these API endpoints, your client apps such as AngularJS, iOS, Android, and others can communicate to your Django backend site independently via REST APIs for user management. [Django-rest-auth][django-rest-auth] library provides a set of REST API endpoints for registration, authentication (including social media authentication), password reset, retrieve and update user details, etc. By having these API endpoints, your client apps such as AngularJS, iOS, Android, and others can communicate to your Django backend site independently via REST APIs for user management.
## django-rest-framework-social-oauth2
[Django-rest-framework-social-oauth2][django-rest-framework-social-oauth2] library provides an easy way to integrate social plugins (facebook, twitter, google, etc.) to your authentication system and an easy oauth2 setup. With this library, you will be able to authenticate users based on external tokens (e.g. facebook access token), convert these tokens to "in-house" oauth2 tokens and use and generate oauth2 tokens to authenticate your users.
## django-rest-knox
[Django-rest-knox][django-rest-knox] library provides models and views to handle token based authentication in a more secure and extensible way than the built-in TokenAuthentication scheme - with Single Page Applications and Mobile clients in mind. It provides per-client tokens, and views to generate them when provided some other authentication (usually basic authentication), to delete the token (providing a server enforced logout) and to delete all tokens (logs out all clients that a user is logged into).
[cite]: http://jacobian.org/writing/rest-worst-practices/ [cite]: http://jacobian.org/writing/rest-worst-practices/
[http401]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2 [http401]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2
[http403]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4 [http403]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4
...@@ -400,3 +408,5 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a ...@@ -400,3 +408,5 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a
[mac]: http://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-05 [mac]: http://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-05
[djoser]: https://github.com/sunscrapers/djoser [djoser]: https://github.com/sunscrapers/djoser
[django-rest-auth]: https://github.com/Tivix/django-rest-auth [django-rest-auth]: https://github.com/Tivix/django-rest-auth
[django-rest-framework-social-oauth2]: https://github.com/PhilipGarnero/django-rest-framework-social-oauth2
[django-rest-knox]: https://github.com/James1345/django-rest-knox
...@@ -51,13 +51,13 @@ Defaults to `False` ...@@ -51,13 +51,13 @@ Defaults to `False`
If set, this gives the default value that will be used for the field if no input value is supplied. If not set the default behavior is to not populate the attribute at all. If set, this gives the default value that will be used for the field if no input value is supplied. If not set the default behavior is to not populate the attribute at all.
May be set to a function or other callable, in which case the value will be evaluated each time it is used. May be set to a function or other callable, in which case the value will be evaluated each time it is used. When called, it will receive no arguments. If the callable has a `set_context` method, that will be called each time before getting the value with the field instance as only argument. This works the same way as for [validators](validators.md#using-set_context).
Note that setting a `default` value implies that the field is not required. Including both the `default` and `required` keyword arguments is invalid and will raise an error. Note that setting a `default` value implies that the field is not required. Including both the `default` and `required` keyword arguments is invalid and will raise an error.
### `source` ### `source`
The name of the attribute that will be used to populate the field. May be a method that only takes a `self` argument, such as `URLField('get_absolute_url')`, or may use dotted notation to traverse attributes, such as `EmailField(source='user.email')`. The name of the attribute that will be used to populate the field. May be a method that only takes a `self` argument, such as `URLField(source='get_absolute_url')`, or may use dotted notation to traverse attributes, such as `EmailField(source='user.email')`.
The value `source='*'` has a special meaning, and is used to indicate that the entire object should be passed through to the field. This can be useful for creating nested representations, or for fields which require access to the complete object in order to determine the output representation. The value `source='*'` has a special meaning, and is used to indicate that the entire object should be passed through to the field. This can be useful for creating nested representations, or for fields which require access to the complete object in order to determine the output representation.
...@@ -85,9 +85,9 @@ A value that should be used for pre-populating the value of HTML form fields. ...@@ -85,9 +85,9 @@ A value that should be used for pre-populating the value of HTML form fields.
### `style` ### `style`
A dictionary of key-value pairs that can be used to control how renderers should render the field. The API for this should still be considered experimental, and will be formalized with the 3.1 release. A dictionary of key-value pairs that can be used to control how renderers should render the field.
Two options are currently used in HTML form generation, `'input_type'` and `'base_template'`. Two examples here are `'input_type'` and `'base_template'`:
# Use <input type="password"> for the input. # Use <input type="password"> for the input.
password = serializers.CharField( password = serializers.CharField(
...@@ -100,7 +100,7 @@ Two options are currently used in HTML form generation, `'input_type'` and `'bas ...@@ -100,7 +100,7 @@ Two options are currently used in HTML form generation, `'input_type'` and `'bas
style = {'base_template': 'radio.html'} style = {'base_template': 'radio.html'}
} }
**Note**: The `style` argument replaces the old-style version 2.x `widget` keyword argument. Because REST framework 3 now uses templated HTML form generation, the `widget` option that was used to support Django built-in widgets can no longer be supported. Version 3.1 is planned to include public API support for customizing HTML form generation. For more details see the [HTML & Forms][html-and-forms] documentation.
--- ---
...@@ -364,6 +364,8 @@ Used by `ModelSerializer` to automatically generate fields if the corresponding ...@@ -364,6 +364,8 @@ Used by `ModelSerializer` to automatically generate fields if the corresponding
- `choices` - A list of valid values, or a list of `(key, display_name)` tuples. - `choices` - A list of valid values, or a list of `(key, display_name)` tuples.
- `allow_blank` - If set to `True` then the empty string should be considered a valid value. If set to `False` then the empty string is considered invalid and will raise a validation error. Defaults to `False`. - `allow_blank` - If set to `True` then the empty string should be considered a valid value. If set to `False` then the empty string is considered invalid and will raise a validation error. Defaults to `False`.
- `html_cutoff` - If set this will be the maximum number of choices that will be displayed by a HTML select drop down. Can be used to ensure that automatically generated ChoiceFields with very large possible selections do not prevent a template from rendering. Defaults to `None`.
- `html_cutoff_text` - If set this will display a textual indicator if the maximum number of items have been cutoff in an HTML select drop down. Defaults to `"More than {count} items…"`
Both the `allow_blank` and `allow_null` are valid options on `ChoiceField`, although it is highly recommended that you only use one and not both. `allow_blank` should be preferred for textual choices, and `allow_null` should be preferred for numeric or other non-textual choices. Both the `allow_blank` and `allow_null` are valid options on `ChoiceField`, although it is highly recommended that you only use one and not both. `allow_blank` should be preferred for textual choices, and `allow_null` should be preferred for numeric or other non-textual choices.
...@@ -375,6 +377,8 @@ A field that can accept a set of zero, one or many values, chosen from a limited ...@@ -375,6 +377,8 @@ A field that can accept a set of zero, one or many values, chosen from a limited
- `choices` - A list of valid values, or a list of `(key, display_name)` tuples. - `choices` - A list of valid values, or a list of `(key, display_name)` tuples.
- `allow_blank` - If set to `True` then the empty string should be considered a valid value. If set to `False` then the empty string is considered invalid and will raise a validation error. Defaults to `False`. - `allow_blank` - If set to `True` then the empty string should be considered a valid value. If set to `False` then the empty string is considered invalid and will raise a validation error. Defaults to `False`.
- `html_cutoff` - If set this will be the maximum number of choices that will be displayed by a HTML select drop down. Can be used to ensure that automatically generated ChoiceFields with very large possible selections do not prevent a template from rendering. Defaults to `None`.
- `html_cutoff_text` - If set this will display a textual indicator if the maximum number of items have been cutoff in an HTML select drop down. Defaults to `"More than {count} items…"`
As with `ChoiceField`, both the `allow_blank` and `allow_null` options are valid, although it is highly recommended that you only use one and not both. `allow_blank` should be preferred for textual choices, and `allow_null` should be preferred for numeric or other non-textual choices. As with `ChoiceField`, both the `allow_blank` and `allow_null` options are valid, although it is highly recommended that you only use one and not both. `allow_blank` should be preferred for textual choices, and `allow_null` should be preferred for numeric or other non-textual choices.
...@@ -455,6 +459,14 @@ You can also use the declarative style, as with `ListField`. For example: ...@@ -455,6 +459,14 @@ You can also use the declarative style, as with `ListField`. For example:
class DocumentField(DictField): class DocumentField(DictField):
child = CharField() child = CharField()
## JSONField
A field class that validates that the incoming data structure consists of valid JSON primitives. In its alternate binary mode, it will represent and validate JSON-encoded binary strings.
**Signature**: `JSONField(binary)`
- `binary` - If set to `True` then the field will output and validate a JSON encoded string, rather that a primitive data structure. Defaults to `False`.
--- ---
# Miscellaneous fields # Miscellaneous fields
...@@ -646,6 +658,7 @@ The [django-rest-framework-gis][django-rest-framework-gis] package provides geog ...@@ -646,6 +658,7 @@ The [django-rest-framework-gis][django-rest-framework-gis] package provides geog
The [django-rest-framework-hstore][django-rest-framework-hstore] package provides an `HStoreField` to support [django-hstore][django-hstore] `DictionaryField` model field. The [django-rest-framework-hstore][django-rest-framework-hstore] package provides an `HStoreField` to support [django-hstore][django-hstore] `DictionaryField` model field.
[cite]: https://docs.djangoproject.com/en/dev/ref/forms/api/#django.forms.Form.cleaned_data [cite]: https://docs.djangoproject.com/en/dev/ref/forms/api/#django.forms.Form.cleaned_data
[html-and-forms]: ../topics/html-and-forms.md
[FILE_UPLOAD_HANDLERS]: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FILE_UPLOAD_HANDLERS [FILE_UPLOAD_HANDLERS]: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FILE_UPLOAD_HANDLERS
[ecma262]: http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15 [ecma262]: http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
[strftime]: http://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior [strftime]: http://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior
......
...@@ -83,6 +83,10 @@ We can override `.get_queryset()` to deal with URLs such as `http://example.com/ ...@@ -83,6 +83,10 @@ We can override `.get_queryset()` to deal with URLs such as `http://example.com/
As well as being able to override the default queryset, REST framework also includes support for generic filtering backends that allow you to easily construct complex searches and filters. As well as being able to override the default queryset, REST framework also includes support for generic filtering backends that allow you to easily construct complex searches and filters.
Generic filters can also present themselves as HTML controls in the browsable API and admin API.
![Filter Example](../img/filter-controls.png)
## Setting filter backends ## Setting filter backends
The default filter backends may be set globally, using the `DEFAULT_FILTER_BACKENDS` setting. For example. The default filter backends may be set globally, using the `DEFAULT_FILTER_BACKENDS` setting. For example.
...@@ -95,9 +99,9 @@ You can also set the filter backends on a per-view, or per-viewset basis, ...@@ -95,9 +99,9 @@ You can also set the filter backends on a per-view, or per-viewset basis,
using the `GenericAPIView` class based views. using the `GenericAPIView` class based views.
from django.contrib.auth.models import User from django.contrib.auth.models import User
from myapp.serializers import UserSerializer from myapp.serializers import UserSerializer
from rest_framework import filters from rest_framework import filters
from rest_framework import generics from rest_framework import generics
class UserListView(generics.ListAPIView): class UserListView(generics.ListAPIView):
queryset = User.objects.all() queryset = User.objects.all()
...@@ -141,6 +145,13 @@ To use REST framework's `DjangoFilterBackend`, first install `django-filter`. ...@@ -141,6 +145,13 @@ To use REST framework's `DjangoFilterBackend`, first install `django-filter`.
pip install django-filter pip install django-filter
If you are using the browsable API or admin API you may also want to install `crispy-forms`, which will enhance the presentation of the filter forms in HTML views, by allowing them to render Bootstrap 3 HTML.
pip install django-crispy-forms
With crispy forms installed, the browsable API will present a filtering control for `DjangoFilterBackend`, like so:
![Django Filter](../../docs/img/django-filter.png)
#### Specifying filter fields #### Specifying filter fields
...@@ -163,6 +174,7 @@ For more advanced filtering requirements you can specify a `FilterSet` class tha ...@@ -163,6 +174,7 @@ For more advanced filtering requirements you can specify a `FilterSet` class tha
import django_filters import django_filters
from myapp.models import Product from myapp.models import Product
from myapp.serializers import ProductSerializer from myapp.serializers import ProductSerializer
from rest_framework import filters
from rest_framework import generics from rest_framework import generics
class ProductFilter(django_filters.FilterSet): class ProductFilter(django_filters.FilterSet):
...@@ -236,6 +248,10 @@ For more details on using filter sets see the [django-filter documentation][djan ...@@ -236,6 +248,10 @@ For more details on using filter sets see the [django-filter documentation][djan
The `SearchFilter` class supports simple single query parameter based searching, and is based on the [Django admin's search functionality][search-django-admin]. The `SearchFilter` class supports simple single query parameter based searching, and is based on the [Django admin's search functionality][search-django-admin].
When in use, the browsable API will include a `SearchFilter` control:
![Search Filter](../../docs/img/search-filter.png)
The `SearchFilter` class will only be applied if the view has a `search_fields` attribute set. The `search_fields` attribute should be a list of names of text type fields on the model, such as `CharField` or `TextField`. The `SearchFilter` class will only be applied if the view has a `search_fields` attribute set. The `search_fields` attribute should be a list of names of text type fields on the model, such as `CharField` or `TextField`.
class UserListView(generics.ListAPIView): class UserListView(generics.ListAPIView):
...@@ -259,6 +275,7 @@ The search behavior may be restricted by prepending various characters to the `s ...@@ -259,6 +275,7 @@ The search behavior may be restricted by prepending various characters to the `s
* '^' Starts-with search. * '^' Starts-with search.
* '=' Exact matches. * '=' Exact matches.
* '@' Full-text search. (Currently only supported Django's MySQL backend.) * '@' Full-text search. (Currently only supported Django's MySQL backend.)
* '$' Regex search.
For example: For example:
...@@ -272,7 +289,11 @@ For more details, see the [Django documentation][search-django-admin]. ...@@ -272,7 +289,11 @@ For more details, see the [Django documentation][search-django-admin].
## OrderingFilter ## OrderingFilter
The `OrderingFilter` class supports simple query parameter controlled ordering of results. By default, the query parameter is named `'ordering'`, but this may by overridden with the `ORDERING_PARAM` setting. The `OrderingFilter` class supports simple query parameter controlled ordering of results.
![Ordering Filter](../../docs/img/ordering-filter.png)
By default, the query parameter is named `'ordering'`, but this may by overridden with the `ORDERING_PARAM` setting.
For example, to order users by username: For example, to order users by username:
...@@ -329,8 +350,6 @@ The `ordering` attribute may be either a string or a list/tuple of strings. ...@@ -329,8 +350,6 @@ The `ordering` attribute may be either a string or a list/tuple of strings.
The `DjangoObjectPermissionsFilter` is intended to be used together with the [`django-guardian`][guardian] package, with custom `'view'` permissions added. The filter will ensure that querysets only returns objects for which the user has the appropriate view permission. The `DjangoObjectPermissionsFilter` is intended to be used together with the [`django-guardian`][guardian] package, with custom `'view'` permissions added. The filter will ensure that querysets only returns objects for which the user has the appropriate view permission.
This filter class must be used with views that provide either a `queryset` or a `model` attribute.
If you're using `DjangoObjectPermissionsFilter`, you'll probably also want to add an appropriate object permissions class, to ensure that users can only operate on instances if they have the appropriate object permissions. The easiest way to do this is to subclass `DjangoObjectPermissions` and add `'view'` permissions to the `perms_map` attribute. If you're using `DjangoObjectPermissionsFilter`, you'll probably also want to add an appropriate object permissions class, to ensure that users can only operate on instances if they have the appropriate object permissions. The easiest way to do this is to subclass `DjangoObjectPermissions` and add `'view'` permissions to the `perms_map` attribute.
A complete example using both `DjangoObjectPermissionsFilter` and `DjangoObjectPermissions` might look something like this. A complete example using both `DjangoObjectPermissionsFilter` and `DjangoObjectPermissions` might look something like this.
...@@ -389,6 +408,14 @@ For example, you might need to restrict users to only being able to see objects ...@@ -389,6 +408,14 @@ For example, you might need to restrict users to only being able to see objects
We could achieve the same behavior by overriding `get_queryset()` on the views, but using a filter backend allows you to more easily add this restriction to multiple views, or to apply it across the entire API. We could achieve the same behavior by overriding `get_queryset()` on the views, but using a filter backend allows you to more easily add this restriction to multiple views, or to apply it across the entire API.
## Customizing the interface
Generic filters may also present an interface in the browsable API. To do so you should implement a `to_html()` method which returns a rendered HTML representation of the filter. This method should have the following signature:
`to_html(self, request, queryset, view)`
The method should return a rendered HTML string.
# Third party packages # Third party packages
The following third party packages provide additional filter implementations. The following third party packages provide additional filter implementations.
...@@ -401,6 +428,10 @@ The [django-rest-framework-filters package][django-rest-framework-filters] works ...@@ -401,6 +428,10 @@ The [django-rest-framework-filters package][django-rest-framework-filters] works
The [djangorestframework-word-filter][django-rest-framework-word-search-filter] developed as alternative to `filters.SearchFilter` which will search full word in text, or exact match. The [djangorestframework-word-filter][django-rest-framework-word-search-filter] developed as alternative to `filters.SearchFilter` which will search full word in text, or exact match.
## Django URL Filter
[django-url-filter][django-url-filter] provides a safe way to filter data via human-friendly URLs. It works very similar to DRF serializers and fields in a sense that they can be nested except they are called filtersets and filters. That provides easy way to filter related data. Also this library is generic-purpose so it can be used to filter other sources of data and not only Django `QuerySet`s.
[cite]: https://docs.djangoproject.com/en/dev/topics/db/queries/#retrieving-specific-objects-with-filters [cite]: https://docs.djangoproject.com/en/dev/topics/db/queries/#retrieving-specific-objects-with-filters
[django-filter]: https://github.com/alex/django-filter [django-filter]: https://github.com/alex/django-filter
[django-filter-docs]: https://django-filter.readthedocs.org/en/latest/index.html [django-filter-docs]: https://django-filter.readthedocs.org/en/latest/index.html
...@@ -411,3 +442,4 @@ The [djangorestframework-word-filter][django-rest-framework-word-search-filter] ...@@ -411,3 +442,4 @@ The [djangorestframework-word-filter][django-rest-framework-word-search-filter]
[search-django-admin]: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields [search-django-admin]: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields
[django-rest-framework-filters]: https://github.com/philipn/django-rest-framework-filters [django-rest-framework-filters]: https://github.com/philipn/django-rest-framework-filters
[django-rest-framework-word-search-filter]: https://github.com/trollknurr/django-rest-framework-word-search-filter [django-rest-framework-word-search-filter]: https://github.com/trollknurr/django-rest-framework-word-search-filter
[django-url-filter]: https://github.com/miki725/django-url-filter
...@@ -69,6 +69,16 @@ If using the `i18n_patterns` function provided by Django, as well as `format_suf ...@@ -69,6 +69,16 @@ If using the `i18n_patterns` function provided by Django, as well as `format_suf
--- ---
## Query parameter formats
An alternative to the format suffixes is to include the requested format in a query parameter. REST framework provides this option by default, and it is used in the browsable API to switch between differing available representations.
To select a representation using its short format, use the `format` query parameter. For example: `http://example.com/organizations/?format=csv`.
The name of this query parameter can be modified using the `URL_FORMAT_OVERRIDE` setting. Set the value to `None` to disable this behavior.
---
## Accept headers vs. format suffixes ## Accept headers vs. format suffixes
There seems to be a view among some of the Web community that filename extensions are not a RESTful pattern, and that `HTTP Accept` headers should always be used instead. There seems to be a view among some of the Web community that filename extensions are not a RESTful pattern, and that `HTTP Accept` headers should always be used instead.
......
...@@ -15,7 +15,7 @@ The pagination API can support either: ...@@ -15,7 +15,7 @@ The pagination API can support either:
The built-in styles currently all use links included as part of the content of the response. This style is more accessible when using the browsable API. The built-in styles currently all use links included as part of the content of the response. This style is more accessible when using the browsable API.
Pagination is only performed automatically if you're using the generic views or viewsets. If you're using a regular `APIView`, you'll need to call into the pagination API yourself to ensure you return a paginated response. See the source code for the `mixins.ListMixin` and `generics.GenericAPIView` classes for an example. Pagination is only performed automatically if you're using the generic views or viewsets. If you're using a regular `APIView`, you'll need to call into the pagination API yourself to ensure you return a paginated response. See the source code for the `mixins.ListModelMixin` and `generics.GenericAPIView` classes for an example.
## Setting the pagination style ## Setting the pagination style
......
...@@ -16,7 +16,7 @@ Relational fields are used to represent model relationships. They can be applie ...@@ -16,7 +16,7 @@ Relational fields are used to represent model relationships. They can be applie
--- ---
#### Inspecting automatically generated relationships. #### Inspecting relationships.
When using the `ModelSerializer` class, serializer fields and relationships will be automatically generated for you. Inspecting these automatically generated fields can be a useful tool for determining how to customize the relationship style. When using the `ModelSerializer` class, serializer fields and relationships will be automatically generated for you. Inspecting these automatically generated fields can be a useful tool for determining how to customize the relationship style.
...@@ -255,7 +255,7 @@ For example, the following serializer: ...@@ -255,7 +255,7 @@ For example, the following serializer:
class TrackSerializer(serializers.ModelSerializer): class TrackSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Track model = Track
fields = ('order', 'title') fields = ('order', 'title', 'duration')
class AlbumSerializer(serializers.ModelSerializer): class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackSerializer(many=True, read_only=True) tracks = TrackSerializer(many=True, read_only=True)
...@@ -288,12 +288,12 @@ Would serialize to a nested representation like this: ...@@ -288,12 +288,12 @@ Would serialize to a nested representation like this:
# Writable nested serializers # Writable nested serializers
Be default nested serializers are read-only. If you want to to support write-operations to a nested serializer field you'll need to create either or both of the `create()` and/or `update()` methods, in order to explicitly specify how the child relationships should be saved. By default nested serializers are read-only. If you want to support write-operations to a nested serializer field you'll need to create `create()` and/or `update()` methods in order to explicitly specify how the child relationships should be saved.
class TrackSerializer(serializers.ModelSerializer): class TrackSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Track model = Track
fields = ('order', 'title') fields = ('order', 'title', 'duration')
class AlbumSerializer(serializers.ModelSerializer): class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackSerializer(many=True) tracks = TrackSerializer(many=True)
...@@ -405,13 +405,15 @@ In this case we'd need to override `HyperlinkedRelatedField` to get the behavior ...@@ -405,13 +405,15 @@ In this case we'd need to override `HyperlinkedRelatedField` to get the behavior
def get_url(self, obj, view_name, request, format): def get_url(self, obj, view_name, request, format):
url_kwargs = { url_kwargs = {
'organization_slug': obj.organization.slug, 'organization_slug': obj.organization.slug,
'customer_pk': obj.pk } 'customer_pk': obj.pk
}
return reverse(view_name, url_kwargs, request=request, format=format) return reverse(view_name, url_kwargs, request=request, format=format)
def get_object(self, view_name, view_args, view_kwargs): def get_object(self, view_name, view_args, view_kwargs):
lookup_kwargs = { lookup_kwargs = {
'organization__slug': view_kwargs['organization_slug'], 'organization__slug': view_kwargs['organization_slug'],
'pk': view_kwargs['customer_pk'] } 'pk': view_kwargs['customer_pk']
}
return self.get_queryset().get(**lookup_kwargs) return self.get_queryset().get(**lookup_kwargs)
Note that if you wanted to use this style together with the generic views then you'd also need to override `.get_object` on the view in order to get the correct lookup behavior. Note that if you wanted to use this style together with the generic views then you'd also need to override `.get_object` on the view in order to get the correct lookup behavior.
...@@ -442,6 +444,25 @@ To provide customized representations for such inputs, override `display_value() ...@@ -442,6 +444,25 @@ To provide customized representations for such inputs, override `display_value()
def display_value(self, instance): def display_value(self, instance):
return 'Track: %s' % (instance.title) return 'Track: %s' % (instance.title)
## Select field cutoffs
When rendered in the browsable API relational fields will default to only displaying a maximum of 1000 selectable items. If more items are present then a disabled option with "More than 1000 items…" will be displayed.
This behavior is intended to prevent a template from being unable to render in an acceptable timespan due to a very large number of relationships being displayed.
There are two keyword arguments you can use to control this behavior:
- `html_cutoff` - If set this will be the maximum number of choices that will be displayed by a HTML select drop down. Set to `None` to disable any limiting. Defaults to `1000`.
- `html_cutoff_text` - If set this will display a textual indicator if the maximum number of items have been cutoff in an HTML select drop down. Defaults to `"More than {count} items…"`
In cases where the cutoff is being enforced you may want to instead use a plain input field in the HTML form. You can do so using the `style` keyword argument. For example:
assigned_to = serializers.SlugRelatedField(
queryset=User.objects.all(),
slug field='username',
style={'base_template': 'input.html'}
)
## Reverse relations ## Reverse relations
Note that reverse relationships are not automatically included by the `ModelSerializer` and `HyperlinkedModelSerializer` classes. To include a reverse relationship, you must explicitly add it to the fields list. For example: Note that reverse relationships are not automatically included by the `ModelSerializer` and `HyperlinkedModelSerializer` classes. To include a reverse relationship, you must explicitly add it to the fields list. For example:
...@@ -482,7 +503,7 @@ For example, given the following model for a tag, which has a generic relationsh ...@@ -482,7 +503,7 @@ For example, given the following model for a tag, which has a generic relationsh
tagged_object = GenericForeignKey('content_type', 'object_id') tagged_object = GenericForeignKey('content_type', 'object_id')
def __unicode__(self): def __unicode__(self):
return self.tag return self.tag_name
And the following two models, which may be have associated tags: And the following two models, which may be have associated tags:
......
...@@ -197,9 +197,19 @@ Note that views that have nested or list serializers for their input won't work ...@@ -197,9 +197,19 @@ Note that views that have nested or list serializers for their input won't work
## HTMLFormRenderer ## HTMLFormRenderer
Renders data returned by a serializer into an HTML form. The output of this renderer does not include the enclosing `<form>` tags or an submit actions, as you'll probably need those to include the desired method and URL. Also note that the `HTMLFormRenderer` does not yet support including field error messages. Renders data returned by a serializer into an HTML form. The output of this renderer does not include the enclosing `<form>` tags, a hidden CSRF input or any submit buttons.
**Note**: The `HTMLFormRenderer` class is intended for internal use with the browsable API and admin interface. It should not be considered a fully documented or stable API. The template used by the `HTMLFormRenderer` class, and the context submitted to it **may be subject to change**. If you need to use this renderer class it is advised that you either make a local copy of the class and templates, or follow the release note on REST framework upgrades closely. This renderer is not intended to be used directly, but can instead be used in templates by passing a serializer instance to the `render_form` template tag.
{% load rest_framework %}
<form action="/submit-report/" method="post">
{% csrf_token %}
{% render_form serializer %}
<input type="submit" value="Save" />
</form>
For more information see the [HTML & Forms][html-and-forms] documentation.
**.media_type**: `text/html` **.media_type**: `text/html`
...@@ -207,7 +217,7 @@ Renders data returned by a serializer into an HTML form. The output of this ren ...@@ -207,7 +217,7 @@ Renders data returned by a serializer into an HTML form. The output of this ren
**.charset**: `utf-8` **.charset**: `utf-8`
**.template**: `'rest_framework/form.html'` **.template**: `'rest_framework/horizontal/form.html'`
## MultiPartRenderer ## MultiPartRenderer
...@@ -455,6 +465,7 @@ Comma-separated values are a plain-text tabular data format, that can be easily ...@@ -455,6 +465,7 @@ Comma-separated values are a plain-text tabular data format, that can be easily
[cite]: https://docs.djangoproject.com/en/dev/ref/template-response/#the-rendering-process [cite]: https://docs.djangoproject.com/en/dev/ref/template-response/#the-rendering-process
[conneg]: content-negotiation.md [conneg]: content-negotiation.md
[html-and-forms]: ../topics/html-and-forms.md
[browser-accept-headers]: http://www.gethifi.com/blog/browser-rest-http-accept-headers [browser-accept-headers]: http://www.gethifi.com/blog/browser-rest-http-accept-headers
[testing]: testing.md [testing]: testing.md
[HATEOAS]: http://timelessrepo.com/haters-gonna-hateoas [HATEOAS]: http://timelessrepo.com/haters-gonna-hateoas
......
...@@ -189,6 +189,12 @@ Your `validate_<field_name>` methods should return the validated value or raise ...@@ -189,6 +189,12 @@ Your `validate_<field_name>` methods should return the validated value or raise
raise serializers.ValidationError("Blog post is not about Django") raise serializers.ValidationError("Blog post is not about Django")
return value return value
---
**Note:** If your `<field_name>` is declared on your serializer with the parameter `required=False` then this validation step will not take place if the field is not included.
---
#### Object-level validation #### Object-level validation
To do any other validation that requires access to multiple fields, add a method called `.validate()` to your `Serializer` subclass. This method takes a single argument, which is a dictionary of field values. It should raise a `ValidationError` if necessary, or just return the validated values. For example: To do any other validation that requires access to multiple fields, add a method called `.validate()` to your `Serializer` subclass. This method takes a single argument, which is a dictionary of field values. It should raise a `ValidationError` if necessary, or just return the validated values. For example:
...@@ -281,7 +287,7 @@ Similarly if a nested representation should be a list of items, you should pass ...@@ -281,7 +287,7 @@ Similarly if a nested representation should be a list of items, you should pass
## Writable nested representations ## Writable nested representations
When dealing with nested representations that support deserializing the data, an errors with nested objects will be nested under the field name of the nested object. When dealing with nested representations that support deserializing the data, any errors with nested objects will be nested under the field name of the nested object.
serializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'}) serializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})
serializer.is_valid() serializer.is_valid()
...@@ -350,7 +356,7 @@ It is possible that a third party package, providing automatic support some kind ...@@ -350,7 +356,7 @@ It is possible that a third party package, providing automatic support some kind
#### Handling saving related instances in model manager classes #### Handling saving related instances in model manager classes
An alternative to saving multiple related instances in the serializer is to write custom model manager classes handle creating the correct instances. An alternative to saving multiple related instances in the serializer is to write custom model manager classes that handle creating the correct instances.
For example, suppose we wanted to ensure that `User` instances and `Profile` instances are always created together as a pair. We might write a custom manager class that looks something like this: For example, suppose we wanted to ensure that `User` instances and `Profile` instances are always created together as a pair. We might write a custom manager class that looks something like this:
...@@ -399,7 +405,7 @@ To serialize a queryset or list of objects instead of a single object instance, ...@@ -399,7 +405,7 @@ To serialize a queryset or list of objects instead of a single object instance,
#### Deserializing multiple objects #### Deserializing multiple objects
The default behavior for deserializing multiple objects is to support multiple object creation, but not support multiple object updates. For more information on how to support or customize either of these cases, see the [ListSerializer](#ListSerializer) documentation below. The default behavior for deserializing multiple objects is to support multiple object creation, but not support multiple object updates. For more information on how to support or customize either of these cases, see the [ListSerializer](#listserializer) documentation below.
## Including extra context ## Including extra context
...@@ -432,6 +438,7 @@ Declaring a `ModelSerializer` looks like this: ...@@ -432,6 +438,7 @@ Declaring a `ModelSerializer` looks like this:
class AccountSerializer(serializers.ModelSerializer): class AccountSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Account model = Account
fields = ('id', 'account_name', 'users', 'created')
By default, all the model fields on the class will be mapped to a corresponding serializer fields. By default, all the model fields on the class will be mapped to a corresponding serializer fields.
...@@ -453,7 +460,7 @@ To do so, open the Django shell, using `python manage.py shell`, then import the ...@@ -453,7 +460,7 @@ To do so, open the Django shell, using `python manage.py shell`, then import the
## Specifying which fields to include ## Specifying which fields to include
If you only want a subset of the default fields to be used in a model serializer, you can do so using `fields` or `exclude` options, just as you would with a `ModelForm`. If you only want a subset of the default fields to be used in a model serializer, you can do so using `fields` or `exclude` options, just as you would with a `ModelForm`. It is strongly recommended that you explicitly set all fields that should be serialized using the `fields` attribute. This will make it less likely to result in unintentionally exposing data when your models change.
For example: For example:
...@@ -462,7 +469,27 @@ For example: ...@@ -462,7 +469,27 @@ For example:
model = Account model = Account
fields = ('id', 'account_name', 'users', 'created') fields = ('id', 'account_name', 'users', 'created')
The names in the `fields` option will normally map to model fields on the model class. You can also set the `fields` attribute to the special value `'__all__'` to indicate that all fields in the model should be used.
For example:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = '__all__'
You can set the `exclude` attribute to a list of fields to be excluded from the serializer.
For example:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
exclude = ('users',)
In the example above, if the `Account` model had 3 fields `account_name`, `users`, and `created`, this will result in the fields `account_name` and `created` to be serialized.
The names in the `fields` and `exclude` attributes will normally map to model fields on the model class.
Alternatively names in the `fields` options can map to properties or methods which take no arguments that exist on the model class. Alternatively names in the `fields` options can map to properties or methods which take no arguments that exist on the model class.
...@@ -524,7 +551,7 @@ Please review the [Validators Documentation](/api-guide/validators/) for details ...@@ -524,7 +551,7 @@ Please review the [Validators Documentation](/api-guide/validators/) for details
## Additional keyword arguments ## Additional keyword arguments
There is also a shortcut allowing you to specify arbitrary additional keyword arguments on fields, using the `extra_kwargs` option. Similarly to `read_only_fields` this means you do not need to explicitly declare the field on the serializer. There is also a shortcut allowing you to specify arbitrary additional keyword arguments on fields, using the `extra_kwargs` option. As in the case of `read_only_fields`, this means you do not need to explicitly declare the field on the serializer.
This option is a dictionary, mapping field names to a dictionary of keyword arguments. For example: This option is a dictionary, mapping field names to a dictionary of keyword arguments. For example:
...@@ -805,7 +832,7 @@ This class implements the same basic API as the `Serializer` class: ...@@ -805,7 +832,7 @@ This class implements the same basic API as the `Serializer` class:
* `.data` - Returns the outgoing primitive representation. * `.data` - Returns the outgoing primitive representation.
* `.is_valid()` - Deserializes and validates incoming data. * `.is_valid()` - Deserializes and validates incoming data.
* `.validated_data` - Returns the validated incoming data. * `.validated_data` - Returns the validated incoming data.
* `.errors` - Returns an errors during validation. * `.errors` - Returns any errors during validation.
* `.save()` - Persists the validated data into an object instance. * `.save()` - Persists the validated data into an object instance.
There are four methods that can be overridden, depending on what functionality you want the serializer class to support: There are four methods that can be overridden, depending on what functionality you want the serializer class to support:
...@@ -1022,6 +1049,13 @@ A new interface for controlling this behavior is currently planned for REST fram ...@@ -1022,6 +1049,13 @@ A new interface for controlling this behavior is currently planned for REST fram
The following third party packages are also available. The following third party packages are also available.
## Django REST marshmallow
The [django-rest-marshmallow][django-rest-marshmallow] package provides an alternative implementation for serializers, using the python [marshmallow][marshmallow] library. It exposes the same API as the REST framework serializers, and can be used as a drop-in replacement in some use-cases.
## Serpy
The [serpy][serpy] package is an alternative implementation for serializers that is built for speed. [Serpy][serpy] serializes complex datatypes to simple native types. The native types can be easily converted to JSON or any other format needed.
## MongoengineModelSerializer ## MongoengineModelSerializer
The [django-rest-framework-mongoengine][mongoengine] package provides a `MongoEngineModelSerializer` serializer class that supports using MongoDB as the storage layer for Django REST framework. The [django-rest-framework-mongoengine][mongoengine] package provides a `MongoEngineModelSerializer` serializer class that supports using MongoDB as the storage layer for Django REST framework.
...@@ -1038,6 +1072,9 @@ The [django-rest-framework-hstore][django-rest-framework-hstore] package provide ...@@ -1038,6 +1072,9 @@ The [django-rest-framework-hstore][django-rest-framework-hstore] package provide
[relations]: relations.md [relations]: relations.md
[model-managers]: https://docs.djangoproject.com/en/dev/topics/db/managers/ [model-managers]: https://docs.djangoproject.com/en/dev/topics/db/managers/
[encapsulation-blogpost]: http://www.dabapps.com/blog/django-models-and-encapsulation/ [encapsulation-blogpost]: http://www.dabapps.com/blog/django-models-and-encapsulation/
[django-rest-marshmallow]: http://tomchristie.github.io/django-rest-marshmallow/
[marshmallow]: https://marshmallow.readthedocs.org/en/latest/
[serpy]: https://github.com/clarkduvall/serpy
[mongoengine]: https://github.com/umutbozkurt/django-rest-framework-mongoengine [mongoengine]: https://github.com/umutbozkurt/django-rest-framework-mongoengine
[django-rest-framework-gis]: https://github.com/djangonauts/django-rest-framework-gis [django-rest-framework-gis]: https://github.com/djangonauts/django-rest-framework-gis
[django-rest-framework-hstore]: https://github.com/djangonauts/django-rest-framework-hstore [django-rest-framework-hstore]: https://github.com/djangonauts/django-rest-framework-hstore
......
...@@ -249,47 +249,23 @@ Default: ...@@ -249,47 +249,23 @@ Default:
--- ---
## Browser overrides ## Content type controls
*The following settings provide URL or form-based overrides of the default browser behavior.* #### URL_FORMAT_OVERRIDE
#### FORM_METHOD_OVERRIDE
The name of a form field that may be used to override the HTTP method of the form.
If the value of this setting is `None` then form method overloading will be disabled.
Default: `'_method'`
#### FORM_CONTENT_OVERRIDE
The name of a form field that may be used to override the content of the form payload. Must be used together with `FORM_CONTENTTYPE_OVERRIDE`.
If either setting is `None` then form content overloading will be disabled.
Default: `'_content'`
#### FORM_CONTENTTYPE_OVERRIDE
The name of a form field that may be used to override the content type of the form payload. Must be used together with `FORM_CONTENT_OVERRIDE`.
If either setting is `None` then form content overloading will be disabled.
Default: `'_content_type'`
#### URL_ACCEPT_OVERRIDE The name of a URL parameter that may be used to override the default content negotiation `Accept` header behavior, by using a `format=…` query parameter in the request URL.
The name of a URL parameter that may be used to override the HTTP `Accept` header. For example: `http://example.com/organizations/?format=csv`
If the value of this setting is `None` then URL accept overloading will be disabled. If the value of this setting is `None` then URL format overrides will be disabled.
Default: `'accept'` Default: `'format'`
#### URL_FORMAT_OVERRIDE #### FORMAT_SUFFIX_KWARG
The name of a URL parameter that may be used to override the default `Accept` header based content negotiation. The name of a parameter in the URL conf that may be used to provide a format suffix. This setting is applied when using `format_suffix_patterns` to include suffixed URL patterns.
If the value of this setting is `None` then URL format overloading will be disabled. For example: `http://example.com/organizations.csv/`
Default: `'format'` Default: `'format'`
...@@ -451,12 +427,6 @@ A string representing the key that should be used for the URL fields generated b ...@@ -451,12 +427,6 @@ A string representing the key that should be used for the URL fields generated b
Default: `'url'` Default: `'url'`
#### FORMAT_SUFFIX_KWARG
The name of a parameter in the URL conf that may be used to provide a format suffix.
Default: `'format'`
#### NUM_PROXIES #### NUM_PROXIES
An integer of 0 or more, that may be used to specify the number of application proxies that the API runs behind. This allows throttling to more accurately identify client IP addresses. If set to `None` then less strict IP matching will be used by the throttle classes. An integer of 0 or more, that may be used to specify the number of application proxies that the API runs behind. This allows throttling to more accurately identify client IP addresses. If set to `None` then less strict IP matching will be used by the throttle classes.
......
...@@ -200,6 +200,7 @@ You can use any of REST framework's test case classes as you would for the regul ...@@ -200,6 +200,7 @@ You can use any of REST framework's test case classes as you would for the regul
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from rest_framework import status from rest_framework import status
from rest_framework.test import APITestCase from rest_framework.test import APITestCase
from myproject.apps.core.models import Account
class AccountTests(APITestCase): class AccountTests(APITestCase):
def test_create_account(self): def test_create_account(self):
...@@ -210,7 +211,8 @@ You can use any of REST framework's test case classes as you would for the regul ...@@ -210,7 +211,8 @@ You can use any of REST framework's test case classes as you would for the regul
data = {'name': 'DabApps'} data = {'name': 'DabApps'}
response = self.client.post(url, data, format='json') response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data, data) self.assertEqual(Account.objects.count(), 1)
self.assertEqual(Account.objects.get().name, 'DabApps')
--- ---
......
...@@ -148,7 +148,7 @@ For example, given the following views... ...@@ -148,7 +148,7 @@ For example, given the following views...
throttle_scope = 'contacts' throttle_scope = 'contacts'
... ...
class ContactDetailView(ApiView): class ContactDetailView(APIView):
throttle_scope = 'contacts' throttle_scope = 'contacts'
... ...
...@@ -184,7 +184,7 @@ If the `.wait()` method is implemented and the request is throttled, then a `Ret ...@@ -184,7 +184,7 @@ If the `.wait()` method is implemented and the request is throttled, then a `Ret
The following is an example of a rate throttle, that will randomly throttle 1 in every 10 requests. The following is an example of a rate throttle, that will randomly throttle 1 in every 10 requests.
class RandomRateThrottle(throttles.BaseThrottle): class RandomRateThrottle(throttling.BaseThrottle):
def allow_request(self, request, view): def allow_request(self, request, view):
return random.randint(1, 10) == 1 return random.randint(1, 10) == 1
......
...@@ -72,7 +72,7 @@ The following settings keys are also used to control versioning: ...@@ -72,7 +72,7 @@ The following settings keys are also used to control versioning:
* `DEFAULT_VERSION`. The value that should be used for `request.version` when no versioning information is present. Defaults to `None`. * `DEFAULT_VERSION`. The value that should be used for `request.version` when no versioning information is present. Defaults to `None`.
* `ALLOWED_VERSIONS`. If set, this value will restrict the set of versions that may be returned by the versioning scheme, and will raise an error if the provided version if not in this set. Note that the value used for the `DEFAULT_VERSION` setting is always considered to be part of the `ALLOWED_VERSIONS` set. Defaults to `None`. * `ALLOWED_VERSIONS`. If set, this value will restrict the set of versions that may be returned by the versioning scheme, and will raise an error if the provided version if not in this set. Note that the value used for the `DEFAULT_VERSION` setting is always considered to be part of the `ALLOWED_VERSIONS` set. Defaults to `None`.
* `VERSION_PARAMETER`. The string that should used for any versioning parameters, such as in the media type or URL query parameters. Defaults to `'version'`. * `VERSION_PARAM`. The string that should used for any versioning parameters, such as in the media type or URL query parameters. Defaults to `'version'`.
You can also set your versioning class plus those three values on a per-view or a per-viewset basis by defining your own versioning scheme and using the `default_version`, `allowed_versions` and `version_param` class variables. For example, if you want to use `URLPathVersioning`: You can also set your versioning class plus those three values on a per-view or a per-viewset basis by defining your own versioning scheme and using the `default_version`, `allowed_versions` and `version_param` class variables. For example, if you want to use `URLPathVersioning`:
......
...@@ -12,9 +12,7 @@ ...@@ -12,9 +12,7 @@
--- ---
**Note**: This is the documentation for the **version 3.2** of REST framework. Documentation for [version 2.4](http://tomchristie.github.io/rest-framework-2-docs/) is also available. **Note**: This is the documentation for the **version 3** of REST framework. Documentation for [version 2](http://tomchristie.github.io/rest-framework-2-docs/) is also available.
For more details see the 3.2 [announcement][3.2-announcement] and [release notes][release-notes].
--- ---
...@@ -31,7 +29,7 @@ For more details see the 3.2 [announcement][3.2-announcement] and [release notes ...@@ -31,7 +29,7 @@ For more details see the 3.2 [announcement][3.2-announcement] and [release notes
<img alt="Django REST Framework" title="Logo by Jake 'Sid' Smith" src="img/logo.png" width="600px" style="display: block; margin: 0 auto 0 auto"> <img alt="Django REST Framework" title="Logo by Jake 'Sid' Smith" src="img/logo.png" width="600px" style="display: block; margin: 0 auto 0 auto">
</p> </p>
Django REST framework is a powerful and flexible toolkit that makes it easy to build Web APIs. Django REST framework is a powerful and flexible toolkit for building Web APIs.
Some reasons you might want to use REST framework: Some reasons you might want to use REST framework:
...@@ -52,13 +50,14 @@ Some reasons you might want to use REST framework: ...@@ -52,13 +50,14 @@ 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.7, 3.2, 3.3, 3.4, 3.5)
* Django (1.5.6+, 1.6.3+, 1.7+, 1.8) * Django (1.7+, 1.8, 1.9)
The following packages are optional: The following packages are optional:
* [Markdown][markdown] (2.1.0+) - Markdown support for the browsable API. * [Markdown][markdown] (2.1.0+) - Markdown support for the browsable API.
* [django-filter][django-filter] (0.9.2+) - Filtering support. * [django-filter][django-filter] (0.9.2+) - Filtering support.
* [django-crispy-forms][django-crispy-forms] - Improved HTML display for filtering.
* [django-guardian][django-guardian] (1.1.1+) - Object level permissions support. * [django-guardian][django-guardian] (1.1.1+) - Object level permissions support.
## Installation ## Installation
...@@ -191,7 +190,9 @@ The API guide is your complete reference manual to all the functionality provide ...@@ -191,7 +190,9 @@ The API guide is your complete reference manual to all the functionality provide
General guides to using REST framework. General guides to using REST framework.
* [Documenting your API][documenting-your-api] * [Documenting your API][documenting-your-api]
* [Internationalization][internationalization]
* [AJAX, CSRF & CORS][ajax-csrf-cors] * [AJAX, CSRF & CORS][ajax-csrf-cors]
* [HTML & Forms][html-and-forms]
* [Browser enhancements][browser-enhancements] * [Browser enhancements][browser-enhancements]
* [The Browsable API][browsableapi] * [The Browsable API][browsableapi]
* [REST, Hypermedia & HATEOAS][rest-hypermedia-hateoas] * [REST, Hypermedia & HATEOAS][rest-hypermedia-hateoas]
...@@ -201,6 +202,7 @@ General guides to using REST framework. ...@@ -201,6 +202,7 @@ General guides to using REST framework.
* [3.0 Announcement][3.0-announcement] * [3.0 Announcement][3.0-announcement]
* [3.1 Announcement][3.1-announcement] * [3.1 Announcement][3.1-announcement]
* [3.2 Announcement][3.2-announcement] * [3.2 Announcement][3.2-announcement]
* [3.3 Announcement][3.3-announcement]
* [Kickstarter Announcement][kickstarter-announcement] * [Kickstarter Announcement][kickstarter-announcement]
* [Funding][funding] * [Funding][funding]
* [Release Notes][release-notes] * [Release Notes][release-notes]
...@@ -304,8 +306,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ...@@ -304,8 +306,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[settings]: api-guide/settings.md [settings]: api-guide/settings.md
[documenting-your-api]: topics/documenting-your-api.md [documenting-your-api]: topics/documenting-your-api.md
[internationalization]: topics/documenting-your-api.md [internationalization]: topics/internationalization.md
[ajax-csrf-cors]: topics/ajax-csrf-cors.md [ajax-csrf-cors]: topics/ajax-csrf-cors.md
[html-and-forms]: topics/html-and-forms.md
[browser-enhancements]: topics/browser-enhancements.md [browser-enhancements]: topics/browser-enhancements.md
[browsableapi]: topics/browsable-api.md [browsableapi]: topics/browsable-api.md
[rest-hypermedia-hateoas]: topics/rest-hypermedia-hateoas.md [rest-hypermedia-hateoas]: topics/rest-hypermedia-hateoas.md
...@@ -315,6 +318,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ...@@ -315,6 +318,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[3.0-announcement]: topics/3.0-announcement.md [3.0-announcement]: topics/3.0-announcement.md
[3.1-announcement]: topics/3.1-announcement.md [3.1-announcement]: topics/3.1-announcement.md
[3.2-announcement]: topics/3.2-announcement.md [3.2-announcement]: topics/3.2-announcement.md
[3.3-announcement]: topics/3.3-announcement.md
[kickstarter-announcement]: topics/kickstarter-announcement.md [kickstarter-announcement]: topics/kickstarter-announcement.md
[funding]: topics/funding.md [funding]: topics/funding.md
[release-notes]: topics/release-notes.md [release-notes]: topics/release-notes.md
......
# Django REST framework 3.3
The 3.3 release marks the final work in the Kickstarter funded series. We'd like to offer a final resounding **thank you** to all our wonderful sponsors and supporters.
The amount of work that has been achieved as a direct result of the funding is immense. We've added a huge amounts of new functionality, resolved nearly 2,000 tickets, and redesigned & refined large parts of the project.
In order to continue driving REST framework forward, we're introducing [monthly paid plans](https://fund.django-rest-framework.org/topics/funding). These plans include various sponsorship rewards, and will ensure that the project remains sustainable and well supported.
We strongly believe that collaboratively funded software development yields outstanding results for a relatively low investment-per-head. If you or your company use REST framework commercially, then we would strongly urge you to participate in this latest funding drive, and help us continue to build an increasingly polished & professional product.
---
## Release notes
Significant new functionality in the 3.3 release includes:
* Filters presented as HTML controls in the browsable API.
* A [forms API][forms-api], allowing serializers to be rendered as HTML forms.
* Django 1.9 support.
* A [`JSONField` serializer field][jsonfield], corresponding to Django 1.9's Postgres `JSONField` model field.
* Browsable API support [via AJAX][ajax-form], rather than server side request overloading.
![Filter Controls](../img/filter-controls.png)
*Example of the new filter controls*
---
## Supported versions
This release drops support for Django 1.5 and 1.6. Django 1.7, 1.8 or 1.9 are now required.
This brings our supported versions into line with Django's [currently supported versions][django-supported-versions]
## Deprecations
The AJAX based support for the browsable API means that there are a number of internal cleanups in the `request` class. For the vast majority of developers this should largely remain transparent:
* To support form based `PUT` and `DELETE`, or to support form content types such as JSON, you should now use the [AJAX forms][ajax-forms] javascript library. This replaces the previous 'method and content type overloading' that required significant internal complexity to the request class.
* The `accept` query parameter is no longer supported by the default content negotiation class. If you require it then you'll need to [use a custom content negotiation class](browser-enhancements.md#url-based-accept-headers).
* The custom `HTTP_X_HTTP_METHOD_OVERRIDE` header is no longer supported by default. If you require it then you'll need to [use custom middleware](browser-enhancements.md#http-header-based-method-overriding).
The following pagination view attributes and settings have been moved into attributes on the pagination class since 3.1. Their usage was formerly deprecated, and has now been removed entirely, in line with the deprecation policy.
* `view.paginate_by` - Use `paginator.page_size` instead.
* `view.page_query_param` - Use `paginator.page_query_param` instead.
* `view.paginate_by_param` - Use `paginator.page_size_query_param` instead.
* `view.max_paginate_by` - Use `paginator.max_page_size` instead.
* `settings.PAGINATE_BY` - Use `paginator.page_size` instead.
* `settings.PAGINATE_BY_PARAM` - Use `paginator.page_size_query_param` instead.
* `settings.MAX_PAGINATE_BY` - Use `paginator.max_page_size` instead.
The `ModelSerializer` and `HyperlinkedModelSerializer` classes should now include either a `fields` or `exclude` option, although the `fields = '__all__'` shortcut may be used. Failing to include either of these two options is currently pending deprecation, and will be removed entirely in the 3.5 release. This behavior brings `ModelSerializer` more closely in line with Django's `ModelForm` behavior.
[forms-api]: html-and-forms.md
[ajax-form]: https://github.com/tomchristie/ajax-form
[jsonfield]: ../../api-guide/fields#jsonfield
[django-supported-versions]: https://www.djangoproject.com/download/#supported-versions
\ No newline at end of file
...@@ -4,67 +4,74 @@ ...@@ -4,67 +4,74 @@
> >
> &mdash; [RESTful Web Services][cite], Leonard Richardson & Sam Ruby. > &mdash; [RESTful Web Services][cite], Leonard Richardson & Sam Ruby.
## Browser based PUT, DELETE, etc... In order to allow the browsable API to function, there are a couple of browser enhancements that REST framework needs to provide.
As of version 3.3.0 onwards these are enabled with javascript, using the [ajax-form][ajax-form] library.
REST framework supports browser-based `PUT`, `DELETE` and other methods, by ## Browser based PUT, DELETE, etc...
overloading `POST` requests using a hidden form field.
Note that this is the same strategy as is used in [Ruby on Rails][rails]. The [AJAX form library][ajax-form] supports browser-based `PUT`, `DELETE` and other methods on HTML forms.
For example, given the following form: After including the library, use the `data-method` attribute on the form, like so:
<form action="/news-items/5" method="POST"> <form action="/" data-method="PUT">
<input type="hidden" name="_method" value="DELETE"> <input name='foo'/>
...
</form> </form>
`request.method` would return `"DELETE"`. Note that prior to 3.3.0, this support was server-side rather than javascript based. The method overloading style (as used in [Ruby on Rails][rails]) is no longer supported due to subtle issues that it introduces in request parsing.
## HTTP header based method overriding ## Browser based submission of non-form content
REST framework also supports method overriding via the semi-standard `X-HTTP-Method-Override` header. This can be useful if you are working with non-form content such as JSON and are working with an older web server and/or hosting provider that doesn't recognise particular HTTP methods such as `PATCH`. For example [Amazon Web Services ELB][aws_elb]. Browser-based submission of content types such as JSON are supported by the [AJAX form library][ajax-form], using form fields with `data-override='content-type'` and `data-override='content'` attributes.
To use it, make a `POST` request, setting the `X-HTTP-Method-Override` header. For example:
For example, making a `PATCH` request via `POST` in jQuery: <form action="/">
<input data-override='content-type' value='application/json' type='hidden'/>
<textarea data-override='content'>{}</textarea>
<input type="submit"/>
</form>
$.ajax({ Note that prior to 3.3.0, this support was server-side rather than javascript based.
url: '/myresource/',
method: 'POST',
headers: {'X-HTTP-Method-Override': 'PATCH'},
...
});
## Browser based submission of non-form content ## URL based format suffixes
Browser-based submission of content types other than form are supported by REST framework can take `?format=json` style URL parameters, which can be a
using form fields named `_content` and `_content_type`: useful shortcut for determining which content type should be returned from
the view.
For example, given the following form: This behavior is controlled using the `URL_FORMAT_OVERRIDE` setting.
<form action="/news-items/5" method="PUT"> ## HTTP header based method overriding
<input type="hidden" name="_content_type" value="application/json">
<input name="_content" value="{'count': 1}">
</form>
`request.content_type` would return `"application/json"`, and Prior to version 3.3.0 the semi extension header `X-HTTP-Method-Override` was supported for overriding the request method. This behavior is no longer in core, but can be adding if needed using middleware.
`request.stream` would return `"{'count': 1}"`
## URL based accept headers For example:
REST framework can take `?accept=application/json` style URL parameters, METHOD_OVERRIDE_HEADER = 'HTTP_X_HTTP_METHOD_OVERRIDE'
which allow the `Accept` header to be overridden.
class MethodOverrideMiddleware(object):
def process_view(self, request, callback, callback_args, callback_kwargs):
if request.method != 'POST':
return
if METHOD_OVERRIDE_HEADER not in request.META:
return
request.method = request.META[METHOD_OVERRIDE_HEADER]
This can be useful for testing the API from a web browser, where you don't ## URL based accept headers
have any control over what is sent in the `Accept` header.
## URL based format suffixes Until version 3.3.0 REST framework included built-in support for `?accept=application/json` style URL parameters, which would allow the `Accept` header to be overridden.
REST framework can take `?format=json` style URL parameters, which can be a Since the introduction of the content negotiation API this behavior is no longer included in core, but may be added using a custom content negotiation class, if needed.
useful shortcut for determining which content type should be returned from
the view. For example:
This is a more concise than using the `accept` override, but it also gives class AcceptQueryParamOverride()
you less control. (For example you can't specify any media type parameters) def get_accept_list(self, request):
header = request.META.get('HTTP_ACCEPT', '*/*')
header = request.query_params.get('_accept', header)
return [token.strip() for token in header.split(',')]
## Doesn't HTML5 support PUT and DELETE forms? ## Doesn't HTML5 support PUT and DELETE forms?
...@@ -74,7 +81,7 @@ was later [dropped from the spec][html5]. There remains ...@@ -74,7 +81,7 @@ was later [dropped from the spec][html5]. There remains
as well as how to support content types other than form-encoded data. as well as how to support content types other than form-encoded data.
[cite]: http://www.amazon.com/Restful-Web-Services-Leonard-Richardson/dp/0596529260 [cite]: http://www.amazon.com/Restful-Web-Services-Leonard-Richardson/dp/0596529260
[ajax-form]: https://github.com/tomchristie/ajax-form
[rails]: http://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-put-or-delete-methods-work [rails]: http://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-put-or-delete-methods-work
[html5]: http://www.w3.org/TR/html5-diff/#changes-2010-06-24 [html5]: http://www.w3.org/TR/html5-diff/#changes-2010-06-24
[put_delete]: http://amundsen.com/examples/put-delete-forms/ [put_delete]: http://amundsen.com/examples/put-delete-forms/
[aws_elb]: https://forums.aws.amazon.com/thread.jspa?messageID=400724
# HTML & Forms
REST framework is suitable for returning both API style responses, and regular HTML pages. Additionally, serializers can used as HTML forms and rendered in templates.
## Rendering HTML
In order to return HTML responses you'll need to either `TemplateHTMLRenderer`, or `StaticHTMLRenderer`.
The `TemplateHTMLRenderer` class expects the response to contain a dictionary of context data, and renders an HTML page based on a template that must be specified either in the view or on the response.
The `StaticHTMLRender` class expects the response to contain a string of the pre-rendered HTML content.
Because static HTML pages typically have different behavior from API responses you'll probably need to write any HTML views explicitly, rather than relying on the built-in generic views.
Here's an example of a view that returns a list of "Profile" instances, rendered in an HTML template:
**views.py**:
from my_project.example.models import Profile
from rest_framework.renderers import TemplateHTMLRenderer
from rest_framework.views import APIView
class ProfileList(APIView):
renderer_classes = [TemplateHTMLRenderer]
template_name = 'profile_list.html'
def get(self, request):
queryset = Profile.objects.all()
return Response({'profiles': queryset})
**profile_list.html**:
<html><body>
<h1>Profiles</h1>
<ul>
{% for profile in profiles %}
<li>{{ profile.name }}</li>
{% endfor %}
</ul>
</body></html>
## Rendering Forms
Serializers may be rendered as forms by using the `render_form` template tag, and including the serializer instance as context to the template.
The following view demonstrates an example of using a serializer in a template for viewing and updating a model instance:
**views.py**:
from django.shortcuts import get_object_or_404
from my_project.example.models import Profile
from rest_framework.renderers import TemplateHTMLRenderer
from rest_framework.views import APIView
class ProfileDetail(APIView):
renderer_classes = [TemplateHTMLRenderer]
template_name = 'profile_detail.html'
def get(self, request, pk):
profile = get_object_or_404(Profile, pk=pk)
serializer = ProfileSerializer(profile)
return Response({'serializer': serializer, 'profile': profile})
def post(self, request, pk):
profile = get_object_or_404(Profile, pk=pk)
serializer = ProfileSerializer(profile)
if not serializer.is_valid():
return Response({'serializer': serializer, 'profile': profile}) return redirect('profile-list')
**profile_detail.html**:
{% load rest_framework %}
<html><body>
<h1>Profile - {{ profile.name }}</h1>
<form action="{% url 'profile-detail' pk=profile.pk '%}" method="POST">
{% csrf_token %}
{% render_form serializer %}
<input type="submit" value="Save">
</form>
</body></html>
### Using template packs
The `render_form` tag takes an optional `template_pack` argument, that specifies which template directory should be used for rendering the form and form fields.
REST framework includes three built-in template packs, all based on Bootstrap 3. The built-in styles are `horizontal`, `vertical`, and `inline`. The default style is `horizontal`. To use any of these template packs you'll want to also include the Bootstrap 3 CSS.
The following HTML will link to a CDN hosted version of the Bootstrap 3 CSS:
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
</head>
Third party packages may include alternate template packs, by bundling a template directory containing the necessary form and field templates.
Let's take a look at how to render each of the three available template packs. For these examples we'll use a single serializer class to present a "Login" form.
class LoginSerializer(serializers.Serializer):
email = serializers.EmailField(
max_length=100,
style={'placeholder': 'Email'}
)
password = serializers.CharField(
max_length=100,
style={'input_type': 'password', 'placeholder': 'Password'}
)
remember_me = serializers.BooleanField() ---
#### `rest_framework/vertical`
Presents form labels above their corresponding control inputs, using the standard Bootstrap layout.
*This is the default template pack.*
{% load rest_framework %}
...
<form action="{% url 'login' %}" method="post" novalidate>
{% csrf_token %}
{% render_form serializer template_pack='rest_framework/vertical' %}
<button type="submit" class="btn btn-default">Sign in</button>
</form>
![Vertical form example](../img/vertical.png)
---
#### `rest_framework/horizontal`
Presents labels and controls alongside each other, using a 2/10 column split.
*This is the form style used in the browsable API and admin renderers.*
{% load rest_framework %}
...
<form class="form-horizontal" action="{% url 'login' %}" method="post" novalidate>
{% csrf_token %}
{% render_form serializer %}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Sign in</button>
</div>
</div>
</form>
![Horizontal form example](../img/horizontal.png)
---
#### `rest_framework/inline`
A compact form style that presents all the controls inline.
{% load rest_framework %}
...
<form class="form-inline" action="{% url 'login' %}" method="post" novalidate>
{% csrf_token %}
{% render_form serializer template_pack='rest_framework/inline' %}
<button type="submit" class="btn btn-default">Sign in</button>
</form>
![Inline form example](../img/inline.png)
## Field styles
Serializer fields can have their rendering style customized by using the `style` keyword argument. This argument is a dictionary of options that control the template and layout used.
The most common way to customize the field style is to use the `base_template` style keyword argument to select which template in the template pack should be use.
For example, to render a `CharField` as an HTML textarea rather than the default HTML input, you would use something like this:
details = serializers.CharField(
max_length=1000,
style={'base_template': 'textarea.html'}
)
If you instead want a field to be rendered using a custom template that is *not part of an included template pack*, you can instead use the `template` style option, to fully specify a template name:
details = serializers.CharField(
max_length=1000,
style={'template': 'my-field-templates/custom-input.html'}
)
Field templates can also use additional style properties, depending on their type. For example, the `textarea.html` template also accepts a `rows` property that can be used to affect the sizing of the control.
details = serializers.CharField(
max_length=1000,
style={'base_template': 'textarea.html', 'rows': 10}
)
The complete list of `base_template` options and their associated style options is listed below.
base_template | Valid field types | Additional style options
----|----|----
input.html | Any string, numeric or date/time field | input_type, placeholder, hide_label
textarea.html | `CharField` | rows, placeholder, hide_label
select.html | `ChoiceField` or relational field types | hide_label
radio.html | `ChoiceField` or relational field types | inline, hide_label
select_multiple.html | `MultipleChoiceField` or relational fields with `many=True` | hide_label
checkbox_multiple.html | `MultipleChoiceField` or relational fields with `many=True` | inline, hide_label
checkbox.html | `BooleanField` | hide_label
fieldset.html | Nested serializer | hide_label
list_fieldset.html | `ListField` or nested serializer with `many=True` | hide_label
...@@ -38,8 +38,48 @@ You can determine your currently installed version using `pip freeze`: ...@@ -38,8 +38,48 @@ You can determine your currently installed version using `pip freeze`:
--- ---
## 3.3.x series
### 3.3.0
**Date**: [27th October 2015][3.3.0-milestone]
* HTML controls for filters. ([#3315][gh3315])
* Forms API. ([#3475][gh3475])
* AJAX browsable API. ([#3410][gh3410])
* Added JSONField. ([#3454][gh3454])
* Correctly map `to_field` when creating `ModelSerializer` relational fields. ([#3526][gh3526])
* Include keyword arguments when mapping `FilePathField` to a serializer field. ([#3536][gh3536])
* Map appropriate model `error_messages` on `ModelSerializer` uniqueness constraints. ([#3435][gh3435])
* Include `max_length` constraint for `ModelSerializer` fields mapped from TextField. ([#3509][gh3509])
* Added support for Django 1.9. ([#3450][gh3450], [#3525][gh3525])
* Removed support for Django 1.5 & 1.6. ([#3421][gh3421], [#3429][gh3429])
* Removed 'south' migrations. ([#3495][gh3495])
## 3.2.x series ## 3.2.x series
### 3.2.4
**Date**: [21th September 2015][3.2.4-milestone].
* Don't error on missing `ViewSet.search_fields` attribute.([#3324][gh3324], [#3323][gh3323])
* Fix `allow_empty` not working on serializers with `many=True`. ([#3361][gh3361], [#3364][gh3364])
* Let `DurationField` accepts integers. ([#3359][gh3359])
* Multi-level dictionaries not supported in multipart requests. ([#3314][gh3314])
* Fix `ListField` truncation on HTTP PATCH ([#3415][gh3415], [#2761][gh2761])
### 3.2.3
**Date**: [24th August 2015][3.2.3-milestone].
* Added `html_cutoff` and `html_cutoff_text` for limiting select dropdowns. ([#3313][gh3313])
* Added regex style to `SearchFilter`. ([#3316][gh3316])
* Resolve issues with setting blank HTML fields. ([#3318][gh3318]) ([#3321][gh3321])
* Correctly display existing 'select multiple' values in browsable API forms. ([#3290][gh3290])
* Resolve duplicated validation message for `IPAddressField`. ([#3249[gh3249]) ([#3250][gh3250])
* Fix to ensure admin renderer continues to work when pagination is disabled. ([#3275][gh3275])
* Resolve error with `LimitOffsetPagination` when count=0, offset=0. ([#3303][gh3303])
### 3.2.2 ### 3.2.2
**Date**: [13th August 2015][3.2.2-milestone]. **Date**: [13th August 2015][3.2.2-milestone].
...@@ -285,7 +325,9 @@ For older release notes, [please see the version 2.x documentation][old-release- ...@@ -285,7 +325,9 @@ For older release notes, [please see the version 2.x documentation][old-release-
[3.1.3-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.1.3+Release%22 [3.1.3-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.1.3+Release%22
[3.2.0-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.2.0+Release%22 [3.2.0-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.2.0+Release%22
[3.2.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.2.1+Release%22 [3.2.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.2.1+Release%22
[3.2.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.2.1+Release%22 [3.2.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.2.2+Release%22
[3.2.3-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.2.3+Release%22
[3.2.4-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.2.4+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
...@@ -486,4 +528,42 @@ For older release notes, [please see the version 2.x documentation][old-release- ...@@ -486,4 +528,42 @@ For older release notes, [please see the version 2.x documentation][old-release-
[gh2776]: https://github.com/tomchristie/django-rest-framework/issues/2776 [gh2776]: https://github.com/tomchristie/django-rest-framework/issues/2776
[gh3261]: https://github.com/tomchristie/django-rest-framework/issues/3261 [gh3261]: https://github.com/tomchristie/django-rest-framework/issues/3261
[gh3260]: https://github.com/tomchristie/django-rest-framework/issues/3260 [gh3260]: https://github.com/tomchristie/django-rest-framework/issues/3260
[gh3241]: https://github.com/tomchristie/django-rest-framework/issues/3241 [gh3241]: https://github.com/tomchristie/django-rest-framework/issues/3241
\ No newline at end of file
<!-- 3.2.3 -->
[gh3249]: https://github.com/tomchristie/django-rest-framework/issues/3249
[gh3250]: https://github.com/tomchristie/django-rest-framework/issues/3250
[gh3275]: https://github.com/tomchristie/django-rest-framework/issues/3275
[gh3288]: https://github.com/tomchristie/django-rest-framework/issues/3288
[gh3290]: https://github.com/tomchristie/django-rest-framework/issues/3290
[gh3303]: https://github.com/tomchristie/django-rest-framework/issues/3303
[gh3313]: https://github.com/tomchristie/django-rest-framework/issues/3313
[gh3316]: https://github.com/tomchristie/django-rest-framework/issues/3316
[gh3318]: https://github.com/tomchristie/django-rest-framework/issues/3318
[gh3321]: https://github.com/tomchristie/django-rest-framework/issues/3321
<!-- 3.2.4 -->
[gh2761]: https://github.com/tomchristie/django-rest-framework/issues/2761
[gh3314]: https://github.com/tomchristie/django-rest-framework/issues/3314
[gh3323]: https://github.com/tomchristie/django-rest-framework/issues/3323
[gh3324]: https://github.com/tomchristie/django-rest-framework/issues/3324
[gh3359]: https://github.com/tomchristie/django-rest-framework/issues/3359
[gh3361]: https://github.com/tomchristie/django-rest-framework/issues/3361
[gh3364]: https://github.com/tomchristie/django-rest-framework/issues/3364
[gh3415]: https://github.com/tomchristie/django-rest-framework/issues/3415
<!-- 3.3.0 -->
[gh3315]: https://github.com/tomchristie/django-rest-framework/issues/3315
[gh3410]: https://github.com/tomchristie/django-rest-framework/issues/3410
[gh3435]: https://github.com/tomchristie/django-rest-framework/issues/3435
[gh3450]: https://github.com/tomchristie/django-rest-framework/issues/3450
[gh3454]: https://github.com/tomchristie/django-rest-framework/issues/3454
[gh3475]: https://github.com/tomchristie/django-rest-framework/issues/3475
[gh3495]: https://github.com/tomchristie/django-rest-framework/issues/3495
[gh3509]: https://github.com/tomchristie/django-rest-framework/issues/3509
[gh3421]: https://github.com/tomchristie/django-rest-framework/issues/3421
[gh3525]: https://github.com/tomchristie/django-rest-framework/issues/3525
[gh3526]: https://github.com/tomchristie/django-rest-framework/issues/3526
[gh3429]: https://github.com/tomchristie/django-rest-framework/issues/3429
[gh3536]: https://github.com/tomchristie/django-rest-framework/issues/3536
...@@ -233,9 +233,11 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque ...@@ -233,9 +233,11 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
### Filtering ### Filtering
* [djangorestframework-chain][djangorestframework-chain] - Allows arbitrary chaining of both relations and lookup filters. * [djangorestframework-chain][djangorestframework-chain] - Allows arbitrary chaining of both relations and lookup filters.
* [django-url-filter][django-url-filter] - Allows a safe way to filter data via human-friendly URLs. It is a generic library which is not tied to DRF but it provides easy integration with DRF.
### Misc ### Misc
* [cookiecutter-django-rest][cookiecutter-django-rest] - A cookiecutter template that takes care of the setup and configuration so you can focus on making your REST apis awesome.
* [djangorestrelationalhyperlink][djangorestrelationalhyperlink] - A hyperlinked serialiser that can can be used to alter relationships via hyperlinks, but otherwise like a hyperlink model serializer. * [djangorestrelationalhyperlink][djangorestrelationalhyperlink] - A hyperlinked serialiser that can can be used to alter relationships via hyperlinks, but otherwise like a hyperlink model serializer.
* [django-rest-swagger][django-rest-swagger] - An API documentation generator for Swagger UI. * [django-rest-swagger][django-rest-swagger] - An API documentation generator for Swagger UI.
* [django-rest-framework-proxy][django-rest-framework-proxy] - Proxy to redirect incoming request to another API server. * [django-rest-framework-proxy][django-rest-framework-proxy] - Proxy to redirect incoming request to another API server.
...@@ -245,6 +247,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque ...@@ -245,6 +247,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
* [django-versatileimagefield][django-versatileimagefield] - Provides a drop-in replacement for Django's stock `ImageField` that makes it easy to serve images in multiple sizes/renditions from a single field. For DRF-specific implementation docs, [click here][django-versatileimagefield-drf-docs]. * [django-versatileimagefield][django-versatileimagefield] - Provides a drop-in replacement for Django's stock `ImageField` that makes it easy to serve images in multiple sizes/renditions from a single field. For DRF-specific implementation docs, [click here][django-versatileimagefield-drf-docs].
* [drf-tracking][drf-tracking] - Utilities to track requests to DRF API views. * [drf-tracking][drf-tracking] - Utilities to track requests to DRF API views.
* [django-rest-framework-braces][django-rest-framework-braces] - Collection of utilities for working with Django Rest Framework. The most notable ones are [FormSerializer](https://django-rest-framework-braces.readthedocs.org/en/latest/overview.html#formserializer) and [SerializerForm](https://django-rest-framework-braces.readthedocs.org/en/latest/overview.html#serializerform), which are adapters between DRF serializers and Django forms. * [django-rest-framework-braces][django-rest-framework-braces] - Collection of utilities for working with Django Rest Framework. The most notable ones are [FormSerializer](https://django-rest-framework-braces.readthedocs.org/en/latest/overview.html#formserializer) and [SerializerForm](https://django-rest-framework-braces.readthedocs.org/en/latest/overview.html#serializerform), which are adapters between DRF serializers and Django forms.
* [drf-haystack][drf-haystack] - Haystack search for Django Rest Framework
## Other Resources ## Other Resources
...@@ -343,3 +346,6 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque ...@@ -343,3 +346,6 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
[drf-tracking]: https://github.com/aschn/drf-tracking [drf-tracking]: https://github.com/aschn/drf-tracking
[django-rest-framework-braces]: https://github.com/dealertrack/django-rest-framework-braces [django-rest-framework-braces]: https://github.com/dealertrack/django-rest-framework-braces
[dry-rest-permissions]: https://github.com/Helioscene/dry-rest-permissions [dry-rest-permissions]: https://github.com/Helioscene/dry-rest-permissions
[django-url-filter]: https://github.com/miki725/django-url-filter
[cookiecutter-django-rest]: https://github.com/agconti/cookiecutter-django-rest
[drf-haystack]: http://drf-haystack.readthedocs.org/en/latest/
...@@ -181,7 +181,7 @@ We can also serialize querysets instead of model instances. To do so we simply ...@@ -181,7 +181,7 @@ We can also serialize querysets instead of model instances. To do so we simply
serializer = SnippetSerializer(Snippet.objects.all(), many=True) serializer = SnippetSerializer(Snippet.objects.all(), many=True)
serializer.data serializer.data
# [{'pk': 1, 'title': u'', 'code': u'foo = "bar"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}, {'pk': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}] # [OrderedDict([('pk', 1), ('title', u''), ('code', u'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('pk', 2), ('title', u''), ('code', u'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('pk', 3), ('title', u''), ('code', u'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]
## Using ModelSerializers ## Using ModelSerializers
......
...@@ -45,7 +45,7 @@ Next we're going to replace the `SnippetList`, `SnippetDetail` and `SnippetHighl ...@@ -45,7 +45,7 @@ Next we're going to replace the `SnippetList`, `SnippetDetail` and `SnippetHighl
return Response(snippet.highlighted) return Response(snippet.highlighted)
def perform_create(self, serializer): def perform_create(self, serializer):
serializer.save(owner=self.request.user) serializer.save(owner=self.request.user)
This time we've used the `ModelViewSet` class in order to get the complete set of default read and write operations. This time we've used the `ModelViewSet` class in order to get the complete set of default read and write operations.
......
...@@ -136,7 +136,7 @@ ...@@ -136,7 +136,7 @@
</div> <!--/.wrapper --> </div> <!--/.wrapper -->
<footer class="span12"> <footer class="span12">
<p>Documentation built with <a href="http://www.mkdocs.org/">MkDocs</a>.</a> <p>Documentation built with <a href="http://www.mkdocs.org/">MkDocs</a>.
</p> </p>
</footer> </footer>
......
...@@ -47,6 +47,7 @@ pages: ...@@ -47,6 +47,7 @@ pages:
- 'Documenting your API': 'topics/documenting-your-api.md' - 'Documenting your API': 'topics/documenting-your-api.md'
- 'Internationalization': 'topics/internationalization.md' - 'Internationalization': 'topics/internationalization.md'
- 'AJAX, CSRF & CORS': 'topics/ajax-csrf-cors.md' - 'AJAX, CSRF & CORS': 'topics/ajax-csrf-cors.md'
- 'HTML & Forms': 'topics/html-and-forms.md'
- 'Browser Enhancements': 'topics/browser-enhancements.md' - 'Browser Enhancements': 'topics/browser-enhancements.md'
- 'The Browsable API': 'topics/browsable-api.md' - 'The Browsable API': 'topics/browsable-api.md'
- 'REST, Hypermedia & HATEOAS': 'topics/rest-hypermedia-hateoas.md' - 'REST, Hypermedia & HATEOAS': 'topics/rest-hypermedia-hateoas.md'
...@@ -56,6 +57,7 @@ pages: ...@@ -56,6 +57,7 @@ pages:
- '3.0 Announcement': 'topics/3.0-announcement.md' - '3.0 Announcement': 'topics/3.0-announcement.md'
- '3.1 Announcement': 'topics/3.1-announcement.md' - '3.1 Announcement': 'topics/3.1-announcement.md'
- '3.2 Announcement': 'topics/3.2-announcement.md' - '3.2 Announcement': 'topics/3.2-announcement.md'
- '3.3 Announcement': 'topics/3.3-announcement.md'
- 'Kickstarter Announcement': 'topics/kickstarter-announcement.md' - 'Kickstarter Announcement': 'topics/kickstarter-announcement.md'
- 'Funding': 'topics/funding.md' - 'Funding': 'topics/funding.md'
- 'Release Notes': 'topics/release-notes.md' - 'Release Notes': 'topics/release-notes.md'
# The base set of requirements for REST framework is actually # The base set of requirements for REST framework is actually
# just Django, but for the purposes of development and testing # just Django, but for the purposes of development and testing
# there are a number of packages that it is useful to install. # there are a number of packages that are useful to install.
# Laying these out as seperate requirements files, allows us to # Laying these out as seperate requirements files, allows us to
# only included the relevent sets when running tox, and ensures # only included the relevent sets when running tox, and ensures
# we are only ever declaring out dependancies in one place. # we are only ever declaring our dependencies in one place.
-r requirements/requirements-optionals.txt -r requirements/requirements-optionals.txt
-r requirements/requirements-testing.txt -r requirements/requirements-testing.txt
......
...@@ -5,4 +5,4 @@ wheel==0.24.0 ...@@ -5,4 +5,4 @@ wheel==0.24.0
twine==1.4.0 twine==1.4.0
# Transifex client for managing translation resources. # Transifex client for managing translation resources.
transifex-client==0.10 transifex-client==0.11b3
...@@ -8,7 +8,7 @@ ______ _____ _____ _____ __ ...@@ -8,7 +8,7 @@ ______ _____ _____ _____ __
""" """
__title__ = 'Django REST framework' __title__ = 'Django REST framework'
__version__ = '3.2.2' __version__ = '3.2.4'
__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'
......
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework import exceptions, serializers from rest_framework import serializers
class AuthTokenSerializer(serializers.Serializer): class AuthTokenSerializer(serializers.Serializer):
...@@ -18,13 +18,13 @@ class AuthTokenSerializer(serializers.Serializer): ...@@ -18,13 +18,13 @@ class AuthTokenSerializer(serializers.Serializer):
if user: if user:
if not user.is_active: if not user.is_active:
msg = _('User account is disabled.') msg = _('User account is disabled.')
raise exceptions.ValidationError(msg) raise serializers.ValidationError(msg)
else: else:
msg = _('Unable to log in with provided credentials.') msg = _('Unable to log in with provided credentials.')
raise exceptions.ValidationError(msg) raise serializers.ValidationError(msg)
else: else:
msg = _('Must include "username" and "password".') msg = _('Must include "username" and "password".')
raise exceptions.ValidationError(msg) raise serializers.ValidationError(msg)
attrs['user'] = user attrs['user'] = user
return attrs return attrs
# -*- coding: utf-8 -*-
from south.db import db
from south.v2 import SchemaMigration
try:
from django.contrib.auth import get_user_model
except ImportError: # django < 1.5
from django.contrib.auth.models import User
else:
User = get_user_model()
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'Token'
db.create_table('authtoken_token', (
('key', self.gf('django.db.models.fields.CharField')(max_length=40, primary_key=True)),
('user', self.gf('django.db.models.fields.related.OneToOneField')(related_name='auth_token', unique=True, to=orm['%s.%s' % (User._meta.app_label, User._meta.object_name)])),
('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
))
db.send_create_signal('authtoken', ['Token'])
def backwards(self, orm):
# Deleting model 'Token'
db.delete_table('authtoken_token')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
"%s.%s" % (User._meta.app_label, User._meta.module_name): {
'Meta': {'object_name': User._meta.module_name, 'db_table': repr(User._meta.db_table)},
},
'authtoken.token': {
'Meta': {'object_name': 'Token'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'primary_key': 'True'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'auth_token'", 'unique': 'True', 'to': "orm['%s.%s']" % (User._meta.app_label, User._meta.object_name)})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
}
}
complete_apps = ['authtoken']
...@@ -50,14 +50,11 @@ def total_seconds(timedelta): ...@@ -50,14 +50,11 @@ def total_seconds(timedelta):
return (timedelta.days * 86400.0) + float(timedelta.seconds) + (timedelta.microseconds / 1000000.0) return (timedelta.days * 86400.0) + float(timedelta.seconds) + (timedelta.microseconds / 1000000.0)
# OrderedDict only available in Python 2.7. def distinct(queryset, base):
# This will always be the case in Django 1.7 and above, as these versions if settings.DATABASES[queryset.db]["ENGINE"] == "django.db.backends.oracle":
# no longer support Python 2.6. # distinct analogue for Oracle users
# For Django <= 1.6 and Python 2.6 fall back to SortedDict. return base.filter(pk__in=set(queryset.values_list('pk', flat=True)))
try: return queryset.distinct()
from collections import OrderedDict
except ImportError:
from django.utils.datastructures import SortedDict as OrderedDict
# contrib.postgres only supported from 1.8 onwards. # contrib.postgres only supported from 1.8 onwards.
...@@ -67,12 +64,27 @@ except ImportError: ...@@ -67,12 +64,27 @@ except ImportError:
postgres_fields = None postgres_fields = None
# JSONField is only supported from 1.9 onwards
try:
from django.contrib.postgres.fields import JSONField
except ImportError:
JSONField = None
# django-filter is optional # django-filter is optional
try: try:
import django_filters import django_filters
except ImportError: except ImportError:
django_filters = None django_filters = None
# django-crispy-forms is optional
try:
import crispy_forms
except ImportError:
crispy_forms = None
if django.VERSION >= (1, 6): if django.VERSION >= (1, 6):
def clean_manytomany_helptext(text): def clean_manytomany_helptext(text):
return text return text
...@@ -84,23 +96,16 @@ else: ...@@ -84,23 +96,16 @@ else:
text = text[:-69] text = text[:-69]
return text return text
# Django-guardian is optional. Import only if guardian is in INSTALLED_APPS # Django-guardian is optional. Import only if guardian is in INSTALLED_APPS
# Fixes (#1712). We keep the try/except for the test suite. # Fixes (#1712). We keep the try/except for the test suite.
guardian = None guardian = None
if 'guardian' in settings.INSTALLED_APPS: try:
try: if 'guardian' in settings.INSTALLED_APPS:
import guardian import guardian
import guardian.shortcuts # Fixes #1624 import guardian.shortcuts # Fixes #1624
except ImportError: except ImportError:
pass pass
def get_model_name(model_cls):
try:
return model_cls._meta.model_name
except AttributeError:
# < 1.6 used module_name instead of model_name
return model_cls._meta.module_name
# MinValueValidator, MaxValueValidator et al. only accept `message` in 1.8+ # MinValueValidator, MaxValueValidator et al. only accept `message` in 1.8+
...@@ -138,32 +143,6 @@ else: ...@@ -138,32 +143,6 @@ else:
super(MaxLengthValidator, self).__init__(*args, **kwargs) super(MaxLengthValidator, self).__init__(*args, **kwargs)
# URLValidator only accepts `message` in 1.6+
if django.VERSION >= (1, 6):
from django.core.validators import URLValidator
else:
from django.core.validators import URLValidator as DjangoURLValidator
class URLValidator(DjangoURLValidator):
def __init__(self, *args, **kwargs):
self.message = kwargs.pop('message', self.message)
super(URLValidator, self).__init__(*args, **kwargs)
# EmailValidator requires explicit regex prior to 1.6+
if django.VERSION >= (1, 6):
from django.core.validators import EmailValidator
else:
from django.core.validators import EmailValidator as DjangoEmailValidator
from django.core.validators import email_re
class EmailValidator(DjangoEmailValidator):
def __init__(self, *args, **kwargs):
super(EmailValidator, self).__init__(email_re, *args, **kwargs)
# PATCH method is not implemented by Django # PATCH method is not implemented by Django
if 'patch' not in View.http_method_names: if 'patch' not in View.http_method_names:
View.http_method_names = View.http_method_names + ['patch'] View.http_method_names = View.http_method_names + ['patch']
......
...@@ -30,10 +30,10 @@ def _force_text_recursive(data): ...@@ -30,10 +30,10 @@ def _force_text_recursive(data):
return ReturnList(ret, serializer=data.serializer) return ReturnList(ret, serializer=data.serializer)
return data return data
elif isinstance(data, dict): elif isinstance(data, dict):
ret = dict([ ret = {
(key, _force_text_recursive(value)) key: _force_text_recursive(value)
for key, value in data.items() for key, value in data.items()
]) }
if isinstance(data, ReturnDict): if isinstance(data, ReturnDict):
return ReturnDict(ret, serializer=data.serializer) return ReturnDict(ret, serializer=data.serializer)
return data return data
......
...@@ -10,12 +10,54 @@ from functools import reduce ...@@ -10,12 +10,54 @@ from functools import reduce
from django.conf import settings from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.db import models from django.db import models
from django.template import Context, loader
from django.utils import six from django.utils import six
from django.utils.translation import ugettext_lazy as _
from rest_framework.compat import django_filters, get_model_name, guardian from rest_framework.compat import (
crispy_forms, distinct, django_filters, guardian
)
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
FilterSet = django_filters and django_filters.FilterSet or None if 'crispy_forms' in settings.INSTALLED_APPS and crispy_forms and django_filters:
# If django-crispy-forms is installed, use it to get a bootstrap3 rendering
# of the DjangoFilterBackend controls when displayed as HTML.
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Submit
class FilterSet(django_filters.FilterSet):
def __init__(self, *args, **kwargs):
super(FilterSet, self).__init__(*args, **kwargs)
for field in self.form.fields.values():
field.help_text = None
layout_components = list(self.form.fields.keys()) + [
Submit('', _('Submit'), css_class='btn-default'),
]
helper = FormHelper()
helper.form_method = 'GET'
helper.template_pack = 'bootstrap3'
helper.layout = Layout(*layout_components)
self.form.helper = helper
filter_template = 'rest_framework/filters/django_filter_crispyforms.html'
elif django_filters:
# If django-crispy-forms is not installed, use the standard
# 'form.as_p' rendering when DjangoFilterBackend is displayed as HTML.
class FilterSet(django_filters.FilterSet):
def __init__(self, *args, **kwargs):
super(FilterSet, self).__init__(*args, **kwargs)
for field in self.form.fields.values():
field.help_text = None
filter_template = 'rest_framework/filters/django_filter.html'
else:
FilterSet = None
filter_template = None
class BaseFilterBackend(object): class BaseFilterBackend(object):
...@@ -35,6 +77,7 @@ class DjangoFilterBackend(BaseFilterBackend): ...@@ -35,6 +77,7 @@ class DjangoFilterBackend(BaseFilterBackend):
A filter backend that uses django-filter. A filter backend that uses django-filter.
""" """
default_filter_set = FilterSet default_filter_set = FilterSet
template = filter_template
def __init__(self): def __init__(self):
assert django_filters, 'Using DjangoFilterBackend, but django-filter is not installed' assert django_filters, 'Using DjangoFilterBackend, but django-filter is not installed'
...@@ -56,7 +99,7 @@ class DjangoFilterBackend(BaseFilterBackend): ...@@ -56,7 +99,7 @@ class DjangoFilterBackend(BaseFilterBackend):
return filter_class return filter_class
if filter_fields: if filter_fields:
class AutoFilterSet(self.default_filter_set): class AutoFilterSet(FilterSet):
class Meta: class Meta:
model = queryset.model model = queryset.model
fields = filter_fields fields = filter_fields
...@@ -73,10 +116,20 @@ class DjangoFilterBackend(BaseFilterBackend): ...@@ -73,10 +116,20 @@ class DjangoFilterBackend(BaseFilterBackend):
return queryset return queryset
def to_html(self, request, queryset, view):
cls = self.get_filter_class(view, queryset)
filter_instance = cls(request.query_params, queryset=queryset)
context = Context({
'filter': filter_instance
})
template = loader.get_template(self.template)
return template.render(context)
class SearchFilter(BaseFilterBackend): class SearchFilter(BaseFilterBackend):
# The URL query parameter used for the search. # The URL query parameter used for the search.
search_param = api_settings.SEARCH_PARAM search_param = api_settings.SEARCH_PARAM
template = 'rest_framework/filters/search.html'
def get_search_terms(self, request): def get_search_terms(self, request):
""" """
...@@ -93,37 +146,55 @@ class SearchFilter(BaseFilterBackend): ...@@ -93,37 +146,55 @@ class SearchFilter(BaseFilterBackend):
return "%s__iexact" % field_name[1:] return "%s__iexact" % field_name[1:]
elif field_name.startswith('@'): elif field_name.startswith('@'):
return "%s__search" % field_name[1:] return "%s__search" % field_name[1:]
if field_name.startswith('$'):
return "%s__iregex" % field_name[1:]
else: else:
return "%s__icontains" % field_name return "%s__icontains" % field_name
def filter_queryset(self, request, queryset, view): def filter_queryset(self, request, queryset, view):
search_fields = getattr(view, 'search_fields', None) search_fields = getattr(view, 'search_fields', None)
search_terms = self.get_search_terms(request)
if not search_fields: if not search_fields or not search_terms:
return queryset return queryset
original_queryset = queryset orm_lookups = [
orm_lookups = [self.construct_search(six.text_type(search_field)) self.construct_search(six.text_type(search_field))
for search_field in search_fields] for search_field in search_fields
]
for search_term in self.get_search_terms(request): base = queryset
or_queries = [models.Q(**{orm_lookup: search_term}) for search_term in search_terms:
for orm_lookup in orm_lookups] queries = [
queryset = queryset.filter(reduce(operator.or_, or_queries)) models.Q(**{orm_lookup: search_term})
for orm_lookup in orm_lookups
]
queryset = queryset.filter(reduce(operator.or_, queries))
if settings.DATABASES[queryset.db]["ENGINE"] == "django.db.backends.oracle": # Filtering against a many-to-many field requires us to
# distinct analogue for Oracle users # call queryset.distinct() in order to avoid duplicate items
queryset = original_queryset.filter(pk__in=set(queryset.values_list('pk', flat=True))) # in the resulting queryset.
else: return distinct(queryset, base)
queryset = queryset.distinct()
return queryset def to_html(self, request, queryset, view):
if not getattr(view, 'search_fields', None):
return ''
term = self.get_search_terms(request)
term = term[0] if term else ''
context = Context({
'param': self.search_param,
'term': term
})
template = loader.get_template(self.template)
return template.render(context)
class OrderingFilter(BaseFilterBackend): class OrderingFilter(BaseFilterBackend):
# The URL query parameter used for the ordering. # The URL query parameter used for the ordering.
ordering_param = api_settings.ORDERING_PARAM ordering_param = api_settings.ORDERING_PARAM
ordering_fields = None ordering_fields = None
template = 'rest_framework/filters/ordering.html'
def get_ordering(self, request, queryset, view): def get_ordering(self, request, queryset, view):
""" """
...@@ -149,7 +220,7 @@ class OrderingFilter(BaseFilterBackend): ...@@ -149,7 +220,7 @@ class OrderingFilter(BaseFilterBackend):
return (ordering,) return (ordering,)
return ordering return ordering
def remove_invalid_fields(self, queryset, fields, view): def get_valid_fields(self, queryset, view):
valid_fields = getattr(view, 'ordering_fields', self.ordering_fields) valid_fields = getattr(view, 'ordering_fields', self.ordering_fields)
if valid_fields is None: if valid_fields is None:
...@@ -160,15 +231,30 @@ class OrderingFilter(BaseFilterBackend): ...@@ -160,15 +231,30 @@ class OrderingFilter(BaseFilterBackend):
"'serializer_class' or 'ordering_fields' attribute.") "'serializer_class' or 'ordering_fields' attribute.")
raise ImproperlyConfigured(msg % self.__class__.__name__) raise ImproperlyConfigured(msg % self.__class__.__name__)
valid_fields = [ valid_fields = [
field.source or field_name (field.source or field_name, field.label)
for field_name, field in serializer_class().fields.items() for field_name, field in serializer_class().fields.items()
if not getattr(field, 'write_only', False) if not getattr(field, 'write_only', False) and not field.source == '*'
] ]
elif valid_fields == '__all__': elif valid_fields == '__all__':
# View explicitly allows filtering on any model field # View explicitly allows filtering on any model field
valid_fields = [field.name for field in queryset.model._meta.fields] valid_fields = [
valid_fields += queryset.query.aggregates.keys() (field.name, getattr(field, 'label', field.name.title()))
for field in queryset.model._meta.fields
]
valid_fields += [
(key, key.title().split('__'))
for key in queryset.query.aggregates.keys()
]
else:
valid_fields = [
(item, item) if isinstance(item, six.string_types) else item
for item in valid_fields
]
return valid_fields
def remove_invalid_fields(self, queryset, fields, view):
valid_fields = [item[0] for item in self.get_valid_fields(queryset, view)]
return [term for term in fields if term.lstrip('-') in valid_fields] return [term for term in fields if term.lstrip('-') in valid_fields]
def filter_queryset(self, request, queryset, view): def filter_queryset(self, request, queryset, view):
...@@ -179,6 +265,25 @@ class OrderingFilter(BaseFilterBackend): ...@@ -179,6 +265,25 @@ class OrderingFilter(BaseFilterBackend):
return queryset return queryset
def get_template_context(self, request, queryset, view):
current = self.get_ordering(request, queryset, view)
current = None if current is None else current[0]
options = []
for key, label in self.get_valid_fields(queryset, view):
options.append((key, '%s - ascending' % label))
options.append(('-' + key, '%s - descending' % label))
return {
'request': request,
'current': current,
'param': self.ordering_param,
'options': options,
}
def to_html(self, request, queryset, view):
template = loader.get_template(self.template)
context = Context(self.get_template_context(request, queryset, view))
return template.render(context)
class DjangoObjectPermissionsFilter(BaseFilterBackend): class DjangoObjectPermissionsFilter(BaseFilterBackend):
""" """
...@@ -196,7 +301,7 @@ class DjangoObjectPermissionsFilter(BaseFilterBackend): ...@@ -196,7 +301,7 @@ class DjangoObjectPermissionsFilter(BaseFilterBackend):
model_cls = queryset.model model_cls = queryset.model
kwargs = { kwargs = {
'app_label': model_cls._meta.app_label, 'app_label': model_cls._meta.app_label,
'model_name': get_model_name(model_cls) 'model_name': model_cls._meta.model_name
} }
permission = self.perm_format % kwargs permission = self.perm_format % kwargs
if guardian.VERSION >= (1, 3): if guardian.VERSION >= (1, 3):
......
...@@ -7,9 +7,9 @@ msgid "" ...@@ -7,9 +7,9 @@ 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-08-06 13:21+0100\n" "POT-Creation-Date: 2015-09-21 10:55+0200\n"
"PO-Revision-Date: 2015-08-06 12:21+0000\n" "PO-Revision-Date: 2015-09-21 08:56+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n" "Last-Translator: Xavier Ordoquy <xordoquy@linovia.com>\n"
"Language-Team: Belarusian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/be/)\n" "Language-Team: Belarusian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/be/)\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"
...@@ -17,40 +17,40 @@ msgstr "" ...@@ -17,40 +17,40 @@ msgstr ""
"Language: be\n" "Language: be\n"
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
#: authentication.py:73 #: authentication.py:72
msgid "Invalid basic header. No credentials provided." msgid "Invalid basic header. No credentials provided."
msgstr "" msgstr ""
#: authentication.py:76 #: authentication.py:75
msgid "Invalid basic header. Credentials string should not contain spaces." msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "" msgstr ""
#: authentication.py:82 #: authentication.py:81
msgid "Invalid basic header. Credentials not correctly base64 encoded." msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "" msgstr ""
#: authentication.py:100 #: authentication.py:98
msgid "Invalid username/password." msgid "Invalid username/password."
msgstr "" msgstr ""
#: authentication.py:103 authentication.py:191 #: authentication.py:101 authentication.py:189
msgid "User inactive or deleted." msgid "User inactive or deleted."
msgstr "" msgstr ""
#: authentication.py:170 #: authentication.py:168
msgid "Invalid token header. No credentials provided." msgid "Invalid token header. No credentials provided."
msgstr "" msgstr ""
#: authentication.py:173 #: authentication.py:171
msgid "Invalid token header. Token string should not contain spaces." msgid "Invalid token header. Token string should not contain spaces."
msgstr "" msgstr ""
#: authentication.py:179 #: authentication.py:177
msgid "" msgid ""
"Invalid token header. Token string should not contain invalid characters." "Invalid token header. Token string should not contain invalid characters."
msgstr "" msgstr ""
#: authentication.py:188 #: authentication.py:186
msgid "Invalid token." msgid "Invalid token."
msgstr "" msgstr ""
...@@ -86,11 +86,12 @@ msgstr "" ...@@ -86,11 +86,12 @@ msgstr ""
msgid "You do not have permission to perform this action." msgid "You do not have permission to perform this action."
msgstr "" msgstr ""
#: exceptions.py:104 views.py:79 #: exceptions.py:104 views.py:81
msgid "Not found." msgid "Not found."
msgstr "" msgstr ""
#: exceptions.py:109 #: exceptions.py:109
#, python-brace-format
msgid "Method \"{method}\" not allowed." msgid "Method \"{method}\" not allowed."
msgstr "" msgstr ""
...@@ -99,6 +100,7 @@ msgid "Could not satisfy the request Accept header." ...@@ -99,6 +100,7 @@ msgid "Could not satisfy the request Accept header."
msgstr "" msgstr ""
#: exceptions.py:132 #: exceptions.py:132
#, python-brace-format
msgid "Unsupported media type \"{media_type}\" in request." msgid "Unsupported media type \"{media_type}\" in request."
msgstr "" msgstr ""
...@@ -106,209 +108,238 @@ msgstr "" ...@@ -106,209 +108,238 @@ msgstr ""
msgid "Request was throttled." msgid "Request was throttled."
msgstr "" msgstr ""
#: fields.py:167 relations.py:173 relations.py:206 validators.py:79 #: fields.py:262 relations.py:191 relations.py:224 validators.py:79
#: validators.py:162 #: validators.py:162
msgid "This field is required." msgid "This field is required."
msgstr "" msgstr ""
#: fields.py:168 #: fields.py:263
msgid "This field may not be null." msgid "This field may not be null."
msgstr "" msgstr ""
#: fields.py:504 fields.py:532 #: fields.py:599 fields.py:627
#, python-brace-format
msgid "\"{input}\" is not a valid boolean." msgid "\"{input}\" is not a valid boolean."
msgstr "" msgstr ""
#: fields.py:567 #: fields.py:662
msgid "This field may not be blank." msgid "This field may not be blank."
msgstr "" msgstr ""
#: fields.py:568 fields.py:1482 #: fields.py:663 fields.py:1594
#, python-brace-format
msgid "Ensure this field has no more than {max_length} characters." msgid "Ensure this field has no more than {max_length} characters."
msgstr "" msgstr ""
#: fields.py:569 #: fields.py:664
#, python-brace-format
msgid "Ensure this field has at least {min_length} characters." msgid "Ensure this field has at least {min_length} characters."
msgstr "" msgstr ""
#: fields.py:606 #: fields.py:701
msgid "Enter a valid email address." msgid "Enter a valid email address."
msgstr "" msgstr ""
#: fields.py:617 #: fields.py:712
msgid "This value does not match the required pattern." msgid "This value does not match the required pattern."
msgstr "" msgstr ""
#: fields.py:628 #: fields.py:723
msgid "" msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or " "Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens." "hyphens."
msgstr "" msgstr ""
#: fields.py:640 #: fields.py:735
msgid "Enter a valid URL." msgid "Enter a valid URL."
msgstr "" msgstr ""
#: fields.py:653 #: fields.py:748
#, python-brace-format
msgid "\"{value}\" is not a valid UUID." msgid "\"{value}\" is not a valid UUID."
msgstr "" msgstr ""
#: fields.py:687 #: fields.py:782
msgid "Enter a valid IPv4 or IPv6 address." msgid "Enter a valid IPv4 or IPv6 address."
msgstr "" msgstr ""
#: fields.py:712 #: fields.py:807
msgid "A valid integer is required." msgid "A valid integer is required."
msgstr "" msgstr ""
#: fields.py:713 fields.py:748 fields.py:781 #: fields.py:808 fields.py:843 fields.py:876
#, python-brace-format
msgid "Ensure this value is less than or equal to {max_value}." msgid "Ensure this value is less than or equal to {max_value}."
msgstr "" msgstr ""
#: fields.py:714 fields.py:749 fields.py:782 #: fields.py:809 fields.py:844 fields.py:877
#, python-brace-format
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 "" msgstr ""
#: fields.py:715 fields.py:750 fields.py:786 #: fields.py:810 fields.py:845 fields.py:881
msgid "String value too large." msgid "String value too large."
msgstr "" msgstr ""
#: fields.py:747 fields.py:780 #: fields.py:842 fields.py:875
msgid "A valid number is required." msgid "A valid number is required."
msgstr "" msgstr ""
#: fields.py:783 #: fields.py:878
#, python-brace-format
msgid "Ensure that there are no more than {max_digits} digits in total." msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "" msgstr ""
#: fields.py:784 #: fields.py:879
#, python-brace-format
msgid "" msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places." "Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "" msgstr ""
#: fields.py:785 #: fields.py:880
#, python-brace-format
msgid "" msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the " "Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point." "decimal point."
msgstr "" msgstr ""
#: fields.py:899 #: fields.py:994
#, python-brace-format
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 ""
#: fields.py:900 #: fields.py:995
msgid "Expected a datetime but got a date." msgid "Expected a datetime but got a date."
msgstr "" msgstr ""
#: fields.py:965 #: fields.py:1060
#, python-brace-format
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 ""
#: fields.py:966 #: fields.py:1061
msgid "Expected a date but got a datetime." msgid "Expected a date but got a datetime."
msgstr "" msgstr ""
#: fields.py:1030 #: fields.py:1125
#, python-brace-format
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 ""
#: fields.py:1085 #: fields.py:1180
#, python-brace-format
msgid "Duration has wrong format. Use one of these formats instead: {format}." msgid "Duration has wrong format. Use one of these formats instead: {format}."
msgstr "" msgstr ""
#: fields.py:1110 fields.py:1154 #: fields.py:1205 fields.py:1254
#, python-brace-format
msgid "\"{input}\" is not a valid choice." msgid "\"{input}\" is not a valid choice."
msgstr "" msgstr ""
#: fields.py:1155 fields.py:1301 relations.py:405 serializers.py:504 #: fields.py:1208 relations.py:58 relations.py:427
#, python-brace-format
msgid "More than {count} items..."
msgstr ""
#: fields.py:1255 fields.py:1401 relations.py:423 serializers.py:504
#, python-brace-format
msgid "Expected a list of items but got type \"{input_type}\"." msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "" msgstr ""
#: fields.py:1156 #: fields.py:1256
msgid "This selection may not be empty." msgid "This selection may not be empty."
msgstr "" msgstr ""
#: fields.py:1194 #: fields.py:1294
#, python-brace-format
msgid "\"{input}\" is not a valid path choice." msgid "\"{input}\" is not a valid path choice."
msgstr "" msgstr ""
#: fields.py:1213 #: fields.py:1313
msgid "No file was submitted." msgid "No file was submitted."
msgstr "" msgstr ""
#: fields.py:1214 #: fields.py:1314
msgid "" msgid ""
"The submitted data was not a file. Check the encoding type on the form." "The submitted data was not a file. Check the encoding type on the form."
msgstr "" msgstr ""
#: fields.py:1215 #: fields.py:1315
msgid "No filename could be determined." msgid "No filename could be determined."
msgstr "" msgstr ""
#: fields.py:1216 #: fields.py:1316
msgid "The submitted file is empty." msgid "The submitted file is empty."
msgstr "" msgstr ""
#: fields.py:1217 #: fields.py:1317
#, python-brace-format
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 ""
#: fields.py:1263 #: fields.py:1363
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 ""
#: fields.py:1302 relations.py:406 serializers.py:505 #: fields.py:1402 relations.py:424 serializers.py:505
msgid "This list may not be empty." msgid "This list may not be empty."
msgstr "" msgstr ""
#: fields.py:1346 #: fields.py:1452
#, python-brace-format
msgid "Expected a dictionary of items but got type \"{input_type}\"." msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "" msgstr ""
#: pagination.py:192 #: pagination.py:192
#, python-brace-format
msgid "Invalid page \"{page_number}\": {message}." msgid "Invalid page \"{page_number}\": {message}."
msgstr "" msgstr ""
#: pagination.py:459 #: pagination.py:462
msgid "Invalid cursor" msgid "Invalid cursor"
msgstr "" msgstr ""
#: relations.py:174 #: relations.py:192
#, python-brace-format
msgid "Invalid pk \"{pk_value}\" - object does not exist." msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "" msgstr ""
#: relations.py:175 #: relations.py:193
#, python-brace-format
msgid "Incorrect type. Expected pk value, received {data_type}." msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "" msgstr ""
#: relations.py:207 #: relations.py:225
msgid "Invalid hyperlink - No URL match." msgid "Invalid hyperlink - No URL match."
msgstr "" msgstr ""
#: relations.py:208 #: relations.py:226
msgid "Invalid hyperlink - Incorrect URL match." msgid "Invalid hyperlink - Incorrect URL match."
msgstr "" msgstr ""
#: relations.py:209 #: relations.py:227
msgid "Invalid hyperlink - Object does not exist." msgid "Invalid hyperlink - Object does not exist."
msgstr "" msgstr ""
#: relations.py:210 #: relations.py:228
#, python-brace-format
msgid "Incorrect type. Expected URL string, received {data_type}." msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "" msgstr ""
#: relations.py:369 #: relations.py:387
#, python-brace-format
msgid "Object with {slug_name}={value} does not exist." msgid "Object with {slug_name}={value} does not exist."
msgstr "" msgstr ""
#: relations.py:370 #: relations.py:388
msgid "Invalid value." msgid "Invalid value."
msgstr "" msgstr ""
#: serializers.py:310 #: serializers.py:310
#, python-brace-format
msgid "Invalid data. Expected a dictionary, but got {datatype}." msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "" msgstr ""
...@@ -329,18 +360,22 @@ msgid "This field must be unique." ...@@ -329,18 +360,22 @@ msgid "This field must be unique."
msgstr "" msgstr ""
#: validators.py:78 #: validators.py:78
#, python-brace-format
msgid "The fields {field_names} must make a unique set." msgid "The fields {field_names} must make a unique set."
msgstr "" msgstr ""
#: validators.py:226 #: validators.py:226
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" date." msgid "This field must be unique for the \"{date_field}\" date."
msgstr "" msgstr ""
#: validators.py:241 #: validators.py:241
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" month." msgid "This field must be unique for the \"{date_field}\" month."
msgstr "" msgstr ""
#: validators.py:254 #: validators.py:254
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" year." msgid "This field must be unique for the \"{date_field}\" year."
msgstr "" msgstr ""
...@@ -360,6 +395,6 @@ msgstr "" ...@@ -360,6 +395,6 @@ msgstr ""
msgid "Invalid version in query parameter." msgid "Invalid version in query parameter."
msgstr "" msgstr ""
#: views.py:86 #: views.py:88
msgid "Permission denied." msgid "Permission denied."
msgstr "" msgstr ""
# 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-09-21 10:55+0200\n"
"PO-Revision-Date: 2015-09-21 08:56+0000\n"
"Last-Translator: Xavier Ordoquy <xordoquy@linovia.com>\n"
"Language-Team: Catalan (Spain) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/ca_ES/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ca_ES\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: authentication.py:72
msgid "Invalid basic header. No credentials provided."
msgstr ""
#: authentication.py:75
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr ""
#: authentication.py:81
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr ""
#: authentication.py:98
msgid "Invalid username/password."
msgstr ""
#: authentication.py:101 authentication.py:189
msgid "User inactive or deleted."
msgstr ""
#: authentication.py:168
msgid "Invalid token header. No credentials provided."
msgstr ""
#: authentication.py:171
msgid "Invalid token header. Token string should not contain spaces."
msgstr ""
#: authentication.py:177
msgid ""
"Invalid token header. Token string should not contain invalid characters."
msgstr ""
#: authentication.py:186
msgid "Invalid token."
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 ""
#: exceptions.py:49
msgid "A server error occurred."
msgstr ""
#: exceptions.py:84
msgid "Malformed request."
msgstr ""
#: exceptions.py:89
msgid "Incorrect authentication credentials."
msgstr ""
#: exceptions.py:94
msgid "Authentication credentials were not provided."
msgstr ""
#: exceptions.py:99
msgid "You do not have permission to perform this action."
msgstr ""
#: exceptions.py:104 views.py:81
msgid "Not found."
msgstr ""
#: exceptions.py:109
#, python-brace-format
msgid "Method \"{method}\" not allowed."
msgstr ""
#: exceptions.py:120
msgid "Could not satisfy the request Accept header."
msgstr ""
#: exceptions.py:132
#, python-brace-format
msgid "Unsupported media type \"{media_type}\" in request."
msgstr ""
#: exceptions.py:145
msgid "Request was throttled."
msgstr ""
#: fields.py:262 relations.py:191 relations.py:224 validators.py:79
#: validators.py:162
msgid "This field is required."
msgstr ""
#: fields.py:263
msgid "This field may not be null."
msgstr ""
#: fields.py:599 fields.py:627
#, python-brace-format
msgid "\"{input}\" is not a valid boolean."
msgstr ""
#: fields.py:662
msgid "This field may not be blank."
msgstr ""
#: fields.py:663 fields.py:1594
#, python-brace-format
msgid "Ensure this field has no more than {max_length} characters."
msgstr ""
#: fields.py:664
#, python-brace-format
msgid "Ensure this field has at least {min_length} characters."
msgstr ""
#: fields.py:701
msgid "Enter a valid email address."
msgstr ""
#: fields.py:712
msgid "This value does not match the required pattern."
msgstr ""
#: fields.py:723
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr ""
#: fields.py:735
msgid "Enter a valid URL."
msgstr ""
#: fields.py:748
#, python-brace-format
msgid "\"{value}\" is not a valid UUID."
msgstr ""
#: fields.py:782
msgid "Enter a valid IPv4 or IPv6 address."
msgstr ""
#: fields.py:807
msgid "A valid integer is required."
msgstr ""
#: fields.py:808 fields.py:843 fields.py:876
#, python-brace-format
msgid "Ensure this value is less than or equal to {max_value}."
msgstr ""
#: fields.py:809 fields.py:844 fields.py:877
#, python-brace-format
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr ""
#: fields.py:810 fields.py:845 fields.py:881
msgid "String value too large."
msgstr ""
#: fields.py:842 fields.py:875
msgid "A valid number is required."
msgstr ""
#: fields.py:878
#, python-brace-format
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr ""
#: fields.py:879
#, python-brace-format
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr ""
#: fields.py:880
#, python-brace-format
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr ""
#: fields.py:994
#, python-brace-format
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr ""
#: fields.py:995
msgid "Expected a datetime but got a date."
msgstr ""
#: fields.py:1060
#, python-brace-format
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr ""
#: fields.py:1061
msgid "Expected a date but got a datetime."
msgstr ""
#: fields.py:1125
#, python-brace-format
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr ""
#: fields.py:1180
#, python-brace-format
msgid "Duration has wrong format. Use one of these formats instead: {format}."
msgstr ""
#: fields.py:1205 fields.py:1254
#, python-brace-format
msgid "\"{input}\" is not a valid choice."
msgstr ""
#: fields.py:1208 relations.py:58 relations.py:427
#, python-brace-format
msgid "More than {count} items..."
msgstr ""
#: fields.py:1255 fields.py:1401 relations.py:423 serializers.py:504
#, python-brace-format
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr ""
#: fields.py:1256
msgid "This selection may not be empty."
msgstr ""
#: fields.py:1294
#, python-brace-format
msgid "\"{input}\" is not a valid path choice."
msgstr ""
#: fields.py:1313
msgid "No file was submitted."
msgstr ""
#: fields.py:1314
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr ""
#: fields.py:1315
msgid "No filename could be determined."
msgstr ""
#: fields.py:1316
msgid "The submitted file is empty."
msgstr ""
#: fields.py:1317
#, python-brace-format
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr ""
#: fields.py:1363
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr ""
#: fields.py:1402 relations.py:424 serializers.py:505
msgid "This list may not be empty."
msgstr ""
#: fields.py:1452
#, python-brace-format
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr ""
#: pagination.py:192
#, python-brace-format
msgid "Invalid page \"{page_number}\": {message}."
msgstr ""
#: pagination.py:462
msgid "Invalid cursor"
msgstr ""
#: relations.py:192
#, python-brace-format
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr ""
#: relations.py:193
#, python-brace-format
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr ""
#: relations.py:225
msgid "Invalid hyperlink - No URL match."
msgstr ""
#: relations.py:226
msgid "Invalid hyperlink - Incorrect URL match."
msgstr ""
#: relations.py:227
msgid "Invalid hyperlink - Object does not exist."
msgstr ""
#: relations.py:228
#, python-brace-format
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr ""
#: relations.py:387
#, python-brace-format
msgid "Object with {slug_name}={value} does not exist."
msgstr ""
#: relations.py:388
msgid "Invalid value."
msgstr ""
#: serializers.py:310
#, python-brace-format
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr ""
#: templates/rest_framework/horizontal/radio.html:2
#: templates/rest_framework/inline/radio.html:2
#: templates/rest_framework/vertical/radio.html:2
msgid "None"
msgstr ""
#: templates/rest_framework/horizontal/select_multiple.html:2
#: templates/rest_framework/inline/select_multiple.html:2
#: templates/rest_framework/vertical/select_multiple.html:2
msgid "No items to select."
msgstr ""
#: validators.py:24
msgid "This field must be unique."
msgstr ""
#: validators.py:78
#, python-brace-format
msgid "The fields {field_names} must make a unique set."
msgstr ""
#: validators.py:226
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" date."
msgstr ""
#: validators.py:241
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" month."
msgstr ""
#: validators.py:254
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" year."
msgstr ""
#: versioning.py:42
msgid "Invalid version in \"Accept\" header."
msgstr ""
#: versioning.py:73 versioning.py:115
msgid "Invalid version in URL path."
msgstr ""
#: versioning.py:144
msgid "Invalid version in hostname."
msgstr ""
#: versioning.py:166
msgid "Invalid version in query parameter."
msgstr ""
#: views.py:88
msgid "Permission denied."
msgstr ""
...@@ -7,9 +7,9 @@ msgid "" ...@@ -7,9 +7,9 @@ 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-08-06 13:21+0100\n" "POT-Creation-Date: 2015-09-21 10:55+0200\n"
"PO-Revision-Date: 2015-08-06 12:21+0000\n" "PO-Revision-Date: 2015-09-21 08:56+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n" "Last-Translator: Xavier Ordoquy <xordoquy@linovia.com>\n"
"Language-Team: English (Canada) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/en_CA/)\n" "Language-Team: English (Canada) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/en_CA/)\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"
...@@ -17,40 +17,40 @@ msgstr "" ...@@ -17,40 +17,40 @@ msgstr ""
"Language: en_CA\n" "Language: en_CA\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: authentication.py:73 #: authentication.py:72
msgid "Invalid basic header. No credentials provided." msgid "Invalid basic header. No credentials provided."
msgstr "" msgstr ""
#: authentication.py:76 #: authentication.py:75
msgid "Invalid basic header. Credentials string should not contain spaces." msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "" msgstr ""
#: authentication.py:82 #: authentication.py:81
msgid "Invalid basic header. Credentials not correctly base64 encoded." msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "" msgstr ""
#: authentication.py:100 #: authentication.py:98
msgid "Invalid username/password." msgid "Invalid username/password."
msgstr "" msgstr ""
#: authentication.py:103 authentication.py:191 #: authentication.py:101 authentication.py:189
msgid "User inactive or deleted." msgid "User inactive or deleted."
msgstr "" msgstr ""
#: authentication.py:170 #: authentication.py:168
msgid "Invalid token header. No credentials provided." msgid "Invalid token header. No credentials provided."
msgstr "" msgstr ""
#: authentication.py:173 #: authentication.py:171
msgid "Invalid token header. Token string should not contain spaces." msgid "Invalid token header. Token string should not contain spaces."
msgstr "" msgstr ""
#: authentication.py:179 #: authentication.py:177
msgid "" msgid ""
"Invalid token header. Token string should not contain invalid characters." "Invalid token header. Token string should not contain invalid characters."
msgstr "" msgstr ""
#: authentication.py:188 #: authentication.py:186
msgid "Invalid token." msgid "Invalid token."
msgstr "" msgstr ""
...@@ -86,11 +86,12 @@ msgstr "" ...@@ -86,11 +86,12 @@ msgstr ""
msgid "You do not have permission to perform this action." msgid "You do not have permission to perform this action."
msgstr "" msgstr ""
#: exceptions.py:104 views.py:79 #: exceptions.py:104 views.py:81
msgid "Not found." msgid "Not found."
msgstr "" msgstr ""
#: exceptions.py:109 #: exceptions.py:109
#, python-brace-format
msgid "Method \"{method}\" not allowed." msgid "Method \"{method}\" not allowed."
msgstr "" msgstr ""
...@@ -99,6 +100,7 @@ msgid "Could not satisfy the request Accept header." ...@@ -99,6 +100,7 @@ msgid "Could not satisfy the request Accept header."
msgstr "" msgstr ""
#: exceptions.py:132 #: exceptions.py:132
#, python-brace-format
msgid "Unsupported media type \"{media_type}\" in request." msgid "Unsupported media type \"{media_type}\" in request."
msgstr "" msgstr ""
...@@ -106,209 +108,238 @@ msgstr "" ...@@ -106,209 +108,238 @@ msgstr ""
msgid "Request was throttled." msgid "Request was throttled."
msgstr "" msgstr ""
#: fields.py:167 relations.py:173 relations.py:206 validators.py:79 #: fields.py:262 relations.py:191 relations.py:224 validators.py:79
#: validators.py:162 #: validators.py:162
msgid "This field is required." msgid "This field is required."
msgstr "" msgstr ""
#: fields.py:168 #: fields.py:263
msgid "This field may not be null." msgid "This field may not be null."
msgstr "" msgstr ""
#: fields.py:504 fields.py:532 #: fields.py:599 fields.py:627
#, python-brace-format
msgid "\"{input}\" is not a valid boolean." msgid "\"{input}\" is not a valid boolean."
msgstr "" msgstr ""
#: fields.py:567 #: fields.py:662
msgid "This field may not be blank." msgid "This field may not be blank."
msgstr "" msgstr ""
#: fields.py:568 fields.py:1482 #: fields.py:663 fields.py:1594
#, python-brace-format
msgid "Ensure this field has no more than {max_length} characters." msgid "Ensure this field has no more than {max_length} characters."
msgstr "" msgstr ""
#: fields.py:569 #: fields.py:664
#, python-brace-format
msgid "Ensure this field has at least {min_length} characters." msgid "Ensure this field has at least {min_length} characters."
msgstr "" msgstr ""
#: fields.py:606 #: fields.py:701
msgid "Enter a valid email address." msgid "Enter a valid email address."
msgstr "" msgstr ""
#: fields.py:617 #: fields.py:712
msgid "This value does not match the required pattern." msgid "This value does not match the required pattern."
msgstr "" msgstr ""
#: fields.py:628 #: fields.py:723
msgid "" msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or " "Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens." "hyphens."
msgstr "" msgstr ""
#: fields.py:640 #: fields.py:735
msgid "Enter a valid URL." msgid "Enter a valid URL."
msgstr "" msgstr ""
#: fields.py:653 #: fields.py:748
#, python-brace-format
msgid "\"{value}\" is not a valid UUID." msgid "\"{value}\" is not a valid UUID."
msgstr "" msgstr ""
#: fields.py:687 #: fields.py:782
msgid "Enter a valid IPv4 or IPv6 address." msgid "Enter a valid IPv4 or IPv6 address."
msgstr "" msgstr ""
#: fields.py:712 #: fields.py:807
msgid "A valid integer is required." msgid "A valid integer is required."
msgstr "" msgstr ""
#: fields.py:713 fields.py:748 fields.py:781 #: fields.py:808 fields.py:843 fields.py:876
#, python-brace-format
msgid "Ensure this value is less than or equal to {max_value}." msgid "Ensure this value is less than or equal to {max_value}."
msgstr "" msgstr ""
#: fields.py:714 fields.py:749 fields.py:782 #: fields.py:809 fields.py:844 fields.py:877
#, python-brace-format
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 "" msgstr ""
#: fields.py:715 fields.py:750 fields.py:786 #: fields.py:810 fields.py:845 fields.py:881
msgid "String value too large." msgid "String value too large."
msgstr "" msgstr ""
#: fields.py:747 fields.py:780 #: fields.py:842 fields.py:875
msgid "A valid number is required." msgid "A valid number is required."
msgstr "" msgstr ""
#: fields.py:783 #: fields.py:878
#, python-brace-format
msgid "Ensure that there are no more than {max_digits} digits in total." msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "" msgstr ""
#: fields.py:784 #: fields.py:879
#, python-brace-format
msgid "" msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places." "Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "" msgstr ""
#: fields.py:785 #: fields.py:880
#, python-brace-format
msgid "" msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the " "Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point." "decimal point."
msgstr "" msgstr ""
#: fields.py:899 #: fields.py:994
#, python-brace-format
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 ""
#: fields.py:900 #: fields.py:995
msgid "Expected a datetime but got a date." msgid "Expected a datetime but got a date."
msgstr "" msgstr ""
#: fields.py:965 #: fields.py:1060
#, python-brace-format
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 ""
#: fields.py:966 #: fields.py:1061
msgid "Expected a date but got a datetime." msgid "Expected a date but got a datetime."
msgstr "" msgstr ""
#: fields.py:1030 #: fields.py:1125
#, python-brace-format
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 ""
#: fields.py:1085 #: fields.py:1180
#, python-brace-format
msgid "Duration has wrong format. Use one of these formats instead: {format}." msgid "Duration has wrong format. Use one of these formats instead: {format}."
msgstr "" msgstr ""
#: fields.py:1110 fields.py:1154 #: fields.py:1205 fields.py:1254
#, python-brace-format
msgid "\"{input}\" is not a valid choice." msgid "\"{input}\" is not a valid choice."
msgstr "" msgstr ""
#: fields.py:1155 fields.py:1301 relations.py:405 serializers.py:504 #: fields.py:1208 relations.py:58 relations.py:427
#, python-brace-format
msgid "More than {count} items..."
msgstr ""
#: fields.py:1255 fields.py:1401 relations.py:423 serializers.py:504
#, python-brace-format
msgid "Expected a list of items but got type \"{input_type}\"." msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "" msgstr ""
#: fields.py:1156 #: fields.py:1256
msgid "This selection may not be empty." msgid "This selection may not be empty."
msgstr "" msgstr ""
#: fields.py:1194 #: fields.py:1294
#, python-brace-format
msgid "\"{input}\" is not a valid path choice." msgid "\"{input}\" is not a valid path choice."
msgstr "" msgstr ""
#: fields.py:1213 #: fields.py:1313
msgid "No file was submitted." msgid "No file was submitted."
msgstr "" msgstr ""
#: fields.py:1214 #: fields.py:1314
msgid "" msgid ""
"The submitted data was not a file. Check the encoding type on the form." "The submitted data was not a file. Check the encoding type on the form."
msgstr "" msgstr ""
#: fields.py:1215 #: fields.py:1315
msgid "No filename could be determined." msgid "No filename could be determined."
msgstr "" msgstr ""
#: fields.py:1216 #: fields.py:1316
msgid "The submitted file is empty." msgid "The submitted file is empty."
msgstr "" msgstr ""
#: fields.py:1217 #: fields.py:1317
#, python-brace-format
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 ""
#: fields.py:1263 #: fields.py:1363
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 ""
#: fields.py:1302 relations.py:406 serializers.py:505 #: fields.py:1402 relations.py:424 serializers.py:505
msgid "This list may not be empty." msgid "This list may not be empty."
msgstr "" msgstr ""
#: fields.py:1346 #: fields.py:1452
#, python-brace-format
msgid "Expected a dictionary of items but got type \"{input_type}\"." msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "" msgstr ""
#: pagination.py:192 #: pagination.py:192
#, python-brace-format
msgid "Invalid page \"{page_number}\": {message}." msgid "Invalid page \"{page_number}\": {message}."
msgstr "" msgstr ""
#: pagination.py:459 #: pagination.py:462
msgid "Invalid cursor" msgid "Invalid cursor"
msgstr "" msgstr ""
#: relations.py:174 #: relations.py:192
#, python-brace-format
msgid "Invalid pk \"{pk_value}\" - object does not exist." msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "" msgstr ""
#: relations.py:175 #: relations.py:193
#, python-brace-format
msgid "Incorrect type. Expected pk value, received {data_type}." msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "" msgstr ""
#: relations.py:207 #: relations.py:225
msgid "Invalid hyperlink - No URL match." msgid "Invalid hyperlink - No URL match."
msgstr "" msgstr ""
#: relations.py:208 #: relations.py:226
msgid "Invalid hyperlink - Incorrect URL match." msgid "Invalid hyperlink - Incorrect URL match."
msgstr "" msgstr ""
#: relations.py:209 #: relations.py:227
msgid "Invalid hyperlink - Object does not exist." msgid "Invalid hyperlink - Object does not exist."
msgstr "" msgstr ""
#: relations.py:210 #: relations.py:228
#, python-brace-format
msgid "Incorrect type. Expected URL string, received {data_type}." msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "" msgstr ""
#: relations.py:369 #: relations.py:387
#, python-brace-format
msgid "Object with {slug_name}={value} does not exist." msgid "Object with {slug_name}={value} does not exist."
msgstr "" msgstr ""
#: relations.py:370 #: relations.py:388
msgid "Invalid value." msgid "Invalid value."
msgstr "" msgstr ""
#: serializers.py:310 #: serializers.py:310
#, python-brace-format
msgid "Invalid data. Expected a dictionary, but got {datatype}." msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "" msgstr ""
...@@ -329,18 +360,22 @@ msgid "This field must be unique." ...@@ -329,18 +360,22 @@ msgid "This field must be unique."
msgstr "" msgstr ""
#: validators.py:78 #: validators.py:78
#, python-brace-format
msgid "The fields {field_names} must make a unique set." msgid "The fields {field_names} must make a unique set."
msgstr "" msgstr ""
#: validators.py:226 #: validators.py:226
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" date." msgid "This field must be unique for the \"{date_field}\" date."
msgstr "" msgstr ""
#: validators.py:241 #: validators.py:241
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" month." msgid "This field must be unique for the \"{date_field}\" month."
msgstr "" msgstr ""
#: validators.py:254 #: validators.py:254
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" year." msgid "This field must be unique for the \"{date_field}\" year."
msgstr "" msgstr ""
...@@ -360,6 +395,6 @@ msgstr "" ...@@ -360,6 +395,6 @@ msgstr ""
msgid "Invalid version in query parameter." msgid "Invalid version in query parameter."
msgstr "" msgstr ""
#: views.py:86 #: views.py:88
msgid "Permission denied." msgid "Permission denied."
msgstr "" msgstr ""
...@@ -8,7 +8,7 @@ msgid "" ...@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-08-06 13:21+0100\n" "POT-Creation-Date: 2015-09-21 10:55+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
...@@ -17,40 +17,40 @@ msgstr "" ...@@ -17,40 +17,40 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: authentication.py:73 #: authentication.py:72
msgid "Invalid basic header. No credentials provided." msgid "Invalid basic header. No credentials provided."
msgstr "" msgstr ""
#: authentication.py:76 #: authentication.py:75
msgid "Invalid basic header. Credentials string should not contain spaces." msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "" msgstr ""
#: authentication.py:82 #: authentication.py:81
msgid "Invalid basic header. Credentials not correctly base64 encoded." msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "" msgstr ""
#: authentication.py:100 #: authentication.py:98
msgid "Invalid username/password." msgid "Invalid username/password."
msgstr "" msgstr ""
#: authentication.py:103 authentication.py:191 #: authentication.py:101 authentication.py:189
msgid "User inactive or deleted." msgid "User inactive or deleted."
msgstr "" msgstr ""
#: authentication.py:170 #: authentication.py:168
msgid "Invalid token header. No credentials provided." msgid "Invalid token header. No credentials provided."
msgstr "" msgstr ""
#: authentication.py:173 #: authentication.py:171
msgid "Invalid token header. Token string should not contain spaces." msgid "Invalid token header. Token string should not contain spaces."
msgstr "" msgstr ""
#: authentication.py:179 #: authentication.py:177
msgid "" msgid ""
"Invalid token header. Token string should not contain invalid characters." "Invalid token header. Token string should not contain invalid characters."
msgstr "" msgstr ""
#: authentication.py:188 #: authentication.py:186
msgid "Invalid token." msgid "Invalid token."
msgstr "" msgstr ""
...@@ -86,11 +86,12 @@ msgstr "" ...@@ -86,11 +86,12 @@ msgstr ""
msgid "You do not have permission to perform this action." msgid "You do not have permission to perform this action."
msgstr "" msgstr ""
#: exceptions.py:104 views.py:79 #: exceptions.py:104 views.py:81
msgid "Not found." msgid "Not found."
msgstr "" msgstr ""
#: exceptions.py:109 #: exceptions.py:109
#, python-brace-format
msgid "Method \"{method}\" not allowed." msgid "Method \"{method}\" not allowed."
msgstr "" msgstr ""
...@@ -99,6 +100,7 @@ msgid "Could not satisfy the request Accept header." ...@@ -99,6 +100,7 @@ msgid "Could not satisfy the request Accept header."
msgstr "" msgstr ""
#: exceptions.py:132 #: exceptions.py:132
#, python-brace-format
msgid "Unsupported media type \"{media_type}\" in request." msgid "Unsupported media type \"{media_type}\" in request."
msgstr "" msgstr ""
...@@ -106,207 +108,236 @@ msgstr "" ...@@ -106,207 +108,236 @@ msgstr ""
msgid "Request was throttled." msgid "Request was throttled."
msgstr "" msgstr ""
#: fields.py:167 relations.py:173 relations.py:206 validators.py:79 #: fields.py:262 relations.py:191 relations.py:224 validators.py:79
#: validators.py:162 #: validators.py:162
msgid "This field is required." msgid "This field is required."
msgstr "" msgstr ""
#: fields.py:168 #: fields.py:263
msgid "This field may not be null." msgid "This field may not be null."
msgstr "" msgstr ""
#: fields.py:504 fields.py:532 #: fields.py:599 fields.py:627
#, python-brace-format
msgid "\"{input}\" is not a valid boolean." msgid "\"{input}\" is not a valid boolean."
msgstr "" msgstr ""
#: fields.py:567 #: fields.py:662
msgid "This field may not be blank." msgid "This field may not be blank."
msgstr "" msgstr ""
#: fields.py:568 fields.py:1482 #: fields.py:663 fields.py:1594
#, python-brace-format
msgid "Ensure this field has no more than {max_length} characters." msgid "Ensure this field has no more than {max_length} characters."
msgstr "" msgstr ""
#: fields.py:569 #: fields.py:664
#, python-brace-format
msgid "Ensure this field has at least {min_length} characters." msgid "Ensure this field has at least {min_length} characters."
msgstr "" msgstr ""
#: fields.py:606 #: fields.py:701
msgid "Enter a valid email address." msgid "Enter a valid email address."
msgstr "" msgstr ""
#: fields.py:617 #: fields.py:712
msgid "This value does not match the required pattern." msgid "This value does not match the required pattern."
msgstr "" msgstr ""
#: fields.py:628 #: fields.py:723
msgid "" msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or " "Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens." "hyphens."
msgstr "" msgstr ""
#: fields.py:640 #: fields.py:735
msgid "Enter a valid URL." msgid "Enter a valid URL."
msgstr "" msgstr ""
#: fields.py:653 #: fields.py:748
#, python-brace-format
msgid "\"{value}\" is not a valid UUID." msgid "\"{value}\" is not a valid UUID."
msgstr "" msgstr ""
#: fields.py:687 #: fields.py:782
msgid "Enter a valid IPv4 or IPv6 address." msgid "Enter a valid IPv4 or IPv6 address."
msgstr "" msgstr ""
#: fields.py:712 #: fields.py:807
msgid "A valid integer is required." msgid "A valid integer is required."
msgstr "" msgstr ""
#: fields.py:713 fields.py:748 fields.py:781 #: fields.py:808 fields.py:843 fields.py:876
#, python-brace-format
msgid "Ensure this value is less than or equal to {max_value}." msgid "Ensure this value is less than or equal to {max_value}."
msgstr "" msgstr ""
#: fields.py:714 fields.py:749 fields.py:782 #: fields.py:809 fields.py:844 fields.py:877
#, python-brace-format
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 "" msgstr ""
#: fields.py:715 fields.py:750 fields.py:786 #: fields.py:810 fields.py:845 fields.py:881
msgid "String value too large." msgid "String value too large."
msgstr "" msgstr ""
#: fields.py:747 fields.py:780 #: fields.py:842 fields.py:875
msgid "A valid number is required." msgid "A valid number is required."
msgstr "" msgstr ""
#: fields.py:783 #: fields.py:878
#, python-brace-format
msgid "Ensure that there are no more than {max_digits} digits in total." msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "" msgstr ""
#: fields.py:784 #: fields.py:879
#, python-brace-format
msgid "Ensure that there are no more than {max_decimal_places} decimal places." msgid "Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "" msgstr ""
#: fields.py:785 #: fields.py:880
#, python-brace-format
msgid "" msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the " "Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point." "decimal point."
msgstr "" msgstr ""
#: fields.py:899 #: fields.py:994
#, python-brace-format
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 ""
#: fields.py:900 #: fields.py:995
msgid "Expected a datetime but got a date." msgid "Expected a datetime but got a date."
msgstr "" msgstr ""
#: fields.py:965 #: fields.py:1060
#, python-brace-format
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 ""
#: fields.py:966 #: fields.py:1061
msgid "Expected a date but got a datetime." msgid "Expected a date but got a datetime."
msgstr "" msgstr ""
#: fields.py:1030 #: fields.py:1125
#, python-brace-format
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 ""
#: fields.py:1085 #: fields.py:1180
#, python-brace-format
msgid "Duration has wrong format. Use one of these formats instead: {format}." msgid "Duration has wrong format. Use one of these formats instead: {format}."
msgstr "" msgstr ""
#: fields.py:1110 fields.py:1154 #: fields.py:1205 fields.py:1254
#, python-brace-format
msgid "\"{input}\" is not a valid choice." msgid "\"{input}\" is not a valid choice."
msgstr "" msgstr ""
#: fields.py:1155 fields.py:1301 relations.py:405 serializers.py:504 #: fields.py:1208 relations.py:58 relations.py:427
#, python-brace-format
msgid "More than {count} items..."
msgstr ""
#: fields.py:1255 fields.py:1401 relations.py:423 serializers.py:504
#, python-brace-format
msgid "Expected a list of items but got type \"{input_type}\"." msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "" msgstr ""
#: fields.py:1156 #: fields.py:1256
msgid "This selection may not be empty." msgid "This selection may not be empty."
msgstr "" msgstr ""
#: fields.py:1194 #: fields.py:1294
#, python-brace-format
msgid "\"{input}\" is not a valid path choice." msgid "\"{input}\" is not a valid path choice."
msgstr "" msgstr ""
#: fields.py:1213 #: fields.py:1313
msgid "No file was submitted." msgid "No file was submitted."
msgstr "" msgstr ""
#: fields.py:1214 #: fields.py:1314
msgid "The submitted data was not a file. Check the encoding type on the form." msgid "The submitted data was not a file. Check the encoding type on the form."
msgstr "" msgstr ""
#: fields.py:1215 #: fields.py:1315
msgid "No filename could be determined." msgid "No filename could be determined."
msgstr "" msgstr ""
#: fields.py:1216 #: fields.py:1316
msgid "The submitted file is empty." msgid "The submitted file is empty."
msgstr "" msgstr ""
#: fields.py:1217 #: fields.py:1317
#, python-brace-format
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 ""
#: fields.py:1263 #: fields.py:1363
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 ""
#: fields.py:1302 relations.py:406 serializers.py:505 #: fields.py:1402 relations.py:424 serializers.py:505
msgid "This list may not be empty." msgid "This list may not be empty."
msgstr "" msgstr ""
#: fields.py:1346 #: fields.py:1452
#, python-brace-format
msgid "Expected a dictionary of items but got type \"{input_type}\"." msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "" msgstr ""
#: pagination.py:192 #: pagination.py:192
#, python-brace-format
msgid "Invalid page \"{page_number}\": {message}." msgid "Invalid page \"{page_number}\": {message}."
msgstr "" msgstr ""
#: pagination.py:459 #: pagination.py:462
msgid "Invalid cursor" msgid "Invalid cursor"
msgstr "" msgstr ""
#: relations.py:174 #: relations.py:192
#, python-brace-format
msgid "Invalid pk \"{pk_value}\" - object does not exist." msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "" msgstr ""
#: relations.py:175 #: relations.py:193
#, python-brace-format
msgid "Incorrect type. Expected pk value, received {data_type}." msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "" msgstr ""
#: relations.py:207 #: relations.py:225
msgid "Invalid hyperlink - No URL match." msgid "Invalid hyperlink - No URL match."
msgstr "" msgstr ""
#: relations.py:208 #: relations.py:226
msgid "Invalid hyperlink - Incorrect URL match." msgid "Invalid hyperlink - Incorrect URL match."
msgstr "" msgstr ""
#: relations.py:209 #: relations.py:227
msgid "Invalid hyperlink - Object does not exist." msgid "Invalid hyperlink - Object does not exist."
msgstr "" msgstr ""
#: relations.py:210 #: relations.py:228
#, python-brace-format
msgid "Incorrect type. Expected URL string, received {data_type}." msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "" msgstr ""
#: relations.py:369 #: relations.py:387
#, python-brace-format
msgid "Object with {slug_name}={value} does not exist." msgid "Object with {slug_name}={value} does not exist."
msgstr "" msgstr ""
#: relations.py:370 #: relations.py:388
msgid "Invalid value." msgid "Invalid value."
msgstr "" msgstr ""
#: serializers.py:310 #: serializers.py:310
#, python-brace-format
msgid "Invalid data. Expected a dictionary, but got {datatype}." msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "" msgstr ""
...@@ -327,18 +358,22 @@ msgid "This field must be unique." ...@@ -327,18 +358,22 @@ msgid "This field must be unique."
msgstr "" msgstr ""
#: validators.py:78 #: validators.py:78
#, python-brace-format
msgid "The fields {field_names} must make a unique set." msgid "The fields {field_names} must make a unique set."
msgstr "" msgstr ""
#: validators.py:226 #: validators.py:226
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" date." msgid "This field must be unique for the \"{date_field}\" date."
msgstr "" msgstr ""
#: validators.py:241 #: validators.py:241
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" month." msgid "This field must be unique for the \"{date_field}\" month."
msgstr "" msgstr ""
#: validators.py:254 #: validators.py:254
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" year." msgid "This field must be unique for the \"{date_field}\" year."
msgstr "" msgstr ""
...@@ -358,6 +393,6 @@ msgstr "" ...@@ -358,6 +393,6 @@ msgstr ""
msgid "Invalid version in query parameter." msgid "Invalid version in query parameter."
msgstr "" msgstr ""
#: views.py:86 #: views.py:88
msgid "Permission denied." msgid "Permission denied."
msgstr "" msgstr ""
...@@ -7,9 +7,9 @@ msgid "" ...@@ -7,9 +7,9 @@ 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-08-06 13:21+0100\n" "POT-Creation-Date: 2015-09-21 10:55+0200\n"
"PO-Revision-Date: 2015-08-06 12:21+0000\n" "PO-Revision-Date: 2015-09-21 08:56+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n" "Last-Translator: Xavier Ordoquy <xordoquy@linovia.com>\n"
"Language-Team: French (Canada) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/fr_CA/)\n" "Language-Team: French (Canada) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/fr_CA/)\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"
...@@ -17,40 +17,40 @@ msgstr "" ...@@ -17,40 +17,40 @@ msgstr ""
"Language: fr_CA\n" "Language: fr_CA\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: authentication.py:73 #: authentication.py:72
msgid "Invalid basic header. No credentials provided." msgid "Invalid basic header. No credentials provided."
msgstr "" msgstr ""
#: authentication.py:76 #: authentication.py:75
msgid "Invalid basic header. Credentials string should not contain spaces." msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "" msgstr ""
#: authentication.py:82 #: authentication.py:81
msgid "Invalid basic header. Credentials not correctly base64 encoded." msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "" msgstr ""
#: authentication.py:100 #: authentication.py:98
msgid "Invalid username/password." msgid "Invalid username/password."
msgstr "" msgstr ""
#: authentication.py:103 authentication.py:191 #: authentication.py:101 authentication.py:189
msgid "User inactive or deleted." msgid "User inactive or deleted."
msgstr "" msgstr ""
#: authentication.py:170 #: authentication.py:168
msgid "Invalid token header. No credentials provided." msgid "Invalid token header. No credentials provided."
msgstr "" msgstr ""
#: authentication.py:173 #: authentication.py:171
msgid "Invalid token header. Token string should not contain spaces." msgid "Invalid token header. Token string should not contain spaces."
msgstr "" msgstr ""
#: authentication.py:179 #: authentication.py:177
msgid "" msgid ""
"Invalid token header. Token string should not contain invalid characters." "Invalid token header. Token string should not contain invalid characters."
msgstr "" msgstr ""
#: authentication.py:188 #: authentication.py:186
msgid "Invalid token." msgid "Invalid token."
msgstr "" msgstr ""
...@@ -86,11 +86,12 @@ msgstr "" ...@@ -86,11 +86,12 @@ msgstr ""
msgid "You do not have permission to perform this action." msgid "You do not have permission to perform this action."
msgstr "" msgstr ""
#: exceptions.py:104 views.py:79 #: exceptions.py:104 views.py:81
msgid "Not found." msgid "Not found."
msgstr "" msgstr ""
#: exceptions.py:109 #: exceptions.py:109
#, python-brace-format
msgid "Method \"{method}\" not allowed." msgid "Method \"{method}\" not allowed."
msgstr "" msgstr ""
...@@ -99,6 +100,7 @@ msgid "Could not satisfy the request Accept header." ...@@ -99,6 +100,7 @@ msgid "Could not satisfy the request Accept header."
msgstr "" msgstr ""
#: exceptions.py:132 #: exceptions.py:132
#, python-brace-format
msgid "Unsupported media type \"{media_type}\" in request." msgid "Unsupported media type \"{media_type}\" in request."
msgstr "" msgstr ""
...@@ -106,209 +108,238 @@ msgstr "" ...@@ -106,209 +108,238 @@ msgstr ""
msgid "Request was throttled." msgid "Request was throttled."
msgstr "" msgstr ""
#: fields.py:167 relations.py:173 relations.py:206 validators.py:79 #: fields.py:262 relations.py:191 relations.py:224 validators.py:79
#: validators.py:162 #: validators.py:162
msgid "This field is required." msgid "This field is required."
msgstr "" msgstr ""
#: fields.py:168 #: fields.py:263
msgid "This field may not be null." msgid "This field may not be null."
msgstr "" msgstr ""
#: fields.py:504 fields.py:532 #: fields.py:599 fields.py:627
#, python-brace-format
msgid "\"{input}\" is not a valid boolean." msgid "\"{input}\" is not a valid boolean."
msgstr "" msgstr ""
#: fields.py:567 #: fields.py:662
msgid "This field may not be blank." msgid "This field may not be blank."
msgstr "" msgstr ""
#: fields.py:568 fields.py:1482 #: fields.py:663 fields.py:1594
#, python-brace-format
msgid "Ensure this field has no more than {max_length} characters." msgid "Ensure this field has no more than {max_length} characters."
msgstr "" msgstr ""
#: fields.py:569 #: fields.py:664
#, python-brace-format
msgid "Ensure this field has at least {min_length} characters." msgid "Ensure this field has at least {min_length} characters."
msgstr "" msgstr ""
#: fields.py:606 #: fields.py:701
msgid "Enter a valid email address." msgid "Enter a valid email address."
msgstr "" msgstr ""
#: fields.py:617 #: fields.py:712
msgid "This value does not match the required pattern." msgid "This value does not match the required pattern."
msgstr "" msgstr ""
#: fields.py:628 #: fields.py:723
msgid "" msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or " "Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens." "hyphens."
msgstr "" msgstr ""
#: fields.py:640 #: fields.py:735
msgid "Enter a valid URL." msgid "Enter a valid URL."
msgstr "" msgstr ""
#: fields.py:653 #: fields.py:748
#, python-brace-format
msgid "\"{value}\" is not a valid UUID." msgid "\"{value}\" is not a valid UUID."
msgstr "" msgstr ""
#: fields.py:687 #: fields.py:782
msgid "Enter a valid IPv4 or IPv6 address." msgid "Enter a valid IPv4 or IPv6 address."
msgstr "" msgstr ""
#: fields.py:712 #: fields.py:807
msgid "A valid integer is required." msgid "A valid integer is required."
msgstr "" msgstr ""
#: fields.py:713 fields.py:748 fields.py:781 #: fields.py:808 fields.py:843 fields.py:876
#, python-brace-format
msgid "Ensure this value is less than or equal to {max_value}." msgid "Ensure this value is less than or equal to {max_value}."
msgstr "" msgstr ""
#: fields.py:714 fields.py:749 fields.py:782 #: fields.py:809 fields.py:844 fields.py:877
#, python-brace-format
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 "" msgstr ""
#: fields.py:715 fields.py:750 fields.py:786 #: fields.py:810 fields.py:845 fields.py:881
msgid "String value too large." msgid "String value too large."
msgstr "" msgstr ""
#: fields.py:747 fields.py:780 #: fields.py:842 fields.py:875
msgid "A valid number is required." msgid "A valid number is required."
msgstr "" msgstr ""
#: fields.py:783 #: fields.py:878
#, python-brace-format
msgid "Ensure that there are no more than {max_digits} digits in total." msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "" msgstr ""
#: fields.py:784 #: fields.py:879
#, python-brace-format
msgid "" msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places." "Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "" msgstr ""
#: fields.py:785 #: fields.py:880
#, python-brace-format
msgid "" msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the " "Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point." "decimal point."
msgstr "" msgstr ""
#: fields.py:899 #: fields.py:994
#, python-brace-format
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 ""
#: fields.py:900 #: fields.py:995
msgid "Expected a datetime but got a date." msgid "Expected a datetime but got a date."
msgstr "" msgstr ""
#: fields.py:965 #: fields.py:1060
#, python-brace-format
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 ""
#: fields.py:966 #: fields.py:1061
msgid "Expected a date but got a datetime." msgid "Expected a date but got a datetime."
msgstr "" msgstr ""
#: fields.py:1030 #: fields.py:1125
#, python-brace-format
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 ""
#: fields.py:1085 #: fields.py:1180
#, python-brace-format
msgid "Duration has wrong format. Use one of these formats instead: {format}." msgid "Duration has wrong format. Use one of these formats instead: {format}."
msgstr "" msgstr ""
#: fields.py:1110 fields.py:1154 #: fields.py:1205 fields.py:1254
#, python-brace-format
msgid "\"{input}\" is not a valid choice." msgid "\"{input}\" is not a valid choice."
msgstr "" msgstr ""
#: fields.py:1155 fields.py:1301 relations.py:405 serializers.py:504 #: fields.py:1208 relations.py:58 relations.py:427
#, python-brace-format
msgid "More than {count} items..."
msgstr ""
#: fields.py:1255 fields.py:1401 relations.py:423 serializers.py:504
#, python-brace-format
msgid "Expected a list of items but got type \"{input_type}\"." msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "" msgstr ""
#: fields.py:1156 #: fields.py:1256
msgid "This selection may not be empty." msgid "This selection may not be empty."
msgstr "" msgstr ""
#: fields.py:1194 #: fields.py:1294
#, python-brace-format
msgid "\"{input}\" is not a valid path choice." msgid "\"{input}\" is not a valid path choice."
msgstr "" msgstr ""
#: fields.py:1213 #: fields.py:1313
msgid "No file was submitted." msgid "No file was submitted."
msgstr "" msgstr ""
#: fields.py:1214 #: fields.py:1314
msgid "" msgid ""
"The submitted data was not a file. Check the encoding type on the form." "The submitted data was not a file. Check the encoding type on the form."
msgstr "" msgstr ""
#: fields.py:1215 #: fields.py:1315
msgid "No filename could be determined." msgid "No filename could be determined."
msgstr "" msgstr ""
#: fields.py:1216 #: fields.py:1316
msgid "The submitted file is empty." msgid "The submitted file is empty."
msgstr "" msgstr ""
#: fields.py:1217 #: fields.py:1317
#, python-brace-format
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 ""
#: fields.py:1263 #: fields.py:1363
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 ""
#: fields.py:1302 relations.py:406 serializers.py:505 #: fields.py:1402 relations.py:424 serializers.py:505
msgid "This list may not be empty." msgid "This list may not be empty."
msgstr "" msgstr ""
#: fields.py:1346 #: fields.py:1452
#, python-brace-format
msgid "Expected a dictionary of items but got type \"{input_type}\"." msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "" msgstr ""
#: pagination.py:192 #: pagination.py:192
#, python-brace-format
msgid "Invalid page \"{page_number}\": {message}." msgid "Invalid page \"{page_number}\": {message}."
msgstr "" msgstr ""
#: pagination.py:459 #: pagination.py:462
msgid "Invalid cursor" msgid "Invalid cursor"
msgstr "" msgstr ""
#: relations.py:174 #: relations.py:192
#, python-brace-format
msgid "Invalid pk \"{pk_value}\" - object does not exist." msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "" msgstr ""
#: relations.py:175 #: relations.py:193
#, python-brace-format
msgid "Incorrect type. Expected pk value, received {data_type}." msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "" msgstr ""
#: relations.py:207 #: relations.py:225
msgid "Invalid hyperlink - No URL match." msgid "Invalid hyperlink - No URL match."
msgstr "" msgstr ""
#: relations.py:208 #: relations.py:226
msgid "Invalid hyperlink - Incorrect URL match." msgid "Invalid hyperlink - Incorrect URL match."
msgstr "" msgstr ""
#: relations.py:209 #: relations.py:227
msgid "Invalid hyperlink - Object does not exist." msgid "Invalid hyperlink - Object does not exist."
msgstr "" msgstr ""
#: relations.py:210 #: relations.py:228
#, python-brace-format
msgid "Incorrect type. Expected URL string, received {data_type}." msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "" msgstr ""
#: relations.py:369 #: relations.py:387
#, python-brace-format
msgid "Object with {slug_name}={value} does not exist." msgid "Object with {slug_name}={value} does not exist."
msgstr "" msgstr ""
#: relations.py:370 #: relations.py:388
msgid "Invalid value." msgid "Invalid value."
msgstr "" msgstr ""
#: serializers.py:310 #: serializers.py:310
#, python-brace-format
msgid "Invalid data. Expected a dictionary, but got {datatype}." msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "" msgstr ""
...@@ -329,18 +360,22 @@ msgid "This field must be unique." ...@@ -329,18 +360,22 @@ msgid "This field must be unique."
msgstr "" msgstr ""
#: validators.py:78 #: validators.py:78
#, python-brace-format
msgid "The fields {field_names} must make a unique set." msgid "The fields {field_names} must make a unique set."
msgstr "" msgstr ""
#: validators.py:226 #: validators.py:226
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" date." msgid "This field must be unique for the \"{date_field}\" date."
msgstr "" msgstr ""
#: validators.py:241 #: validators.py:241
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" month." msgid "This field must be unique for the \"{date_field}\" month."
msgstr "" msgstr ""
#: validators.py:254 #: validators.py:254
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" year." msgid "This field must be unique for the \"{date_field}\" year."
msgstr "" msgstr ""
...@@ -360,6 +395,6 @@ msgstr "" ...@@ -360,6 +395,6 @@ msgstr ""
msgid "Invalid version in query parameter." msgid "Invalid version in query parameter."
msgstr "" msgstr ""
#: views.py:86 #: views.py:88
msgid "Permission denied." msgid "Permission denied."
msgstr "" msgstr ""
...@@ -7,9 +7,9 @@ msgid "" ...@@ -7,9 +7,9 @@ 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-08-06 13:21+0100\n" "POT-Creation-Date: 2015-09-21 10:55+0200\n"
"PO-Revision-Date: 2015-08-06 12:21+0000\n" "PO-Revision-Date: 2015-09-21 08:56+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n" "Last-Translator: Xavier Ordoquy <xordoquy@linovia.com>\n"
"Language-Team: Galician (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/gl/)\n" "Language-Team: Galician (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/gl/)\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"
...@@ -17,40 +17,40 @@ msgstr "" ...@@ -17,40 +17,40 @@ msgstr ""
"Language: gl\n" "Language: gl\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: authentication.py:73 #: authentication.py:72
msgid "Invalid basic header. No credentials provided." msgid "Invalid basic header. No credentials provided."
msgstr "" msgstr ""
#: authentication.py:76 #: authentication.py:75
msgid "Invalid basic header. Credentials string should not contain spaces." msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "" msgstr ""
#: authentication.py:82 #: authentication.py:81
msgid "Invalid basic header. Credentials not correctly base64 encoded." msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "" msgstr ""
#: authentication.py:100 #: authentication.py:98
msgid "Invalid username/password." msgid "Invalid username/password."
msgstr "" msgstr ""
#: authentication.py:103 authentication.py:191 #: authentication.py:101 authentication.py:189
msgid "User inactive or deleted." msgid "User inactive or deleted."
msgstr "" msgstr ""
#: authentication.py:170 #: authentication.py:168
msgid "Invalid token header. No credentials provided." msgid "Invalid token header. No credentials provided."
msgstr "" msgstr ""
#: authentication.py:173 #: authentication.py:171
msgid "Invalid token header. Token string should not contain spaces." msgid "Invalid token header. Token string should not contain spaces."
msgstr "" msgstr ""
#: authentication.py:179 #: authentication.py:177
msgid "" msgid ""
"Invalid token header. Token string should not contain invalid characters." "Invalid token header. Token string should not contain invalid characters."
msgstr "" msgstr ""
#: authentication.py:188 #: authentication.py:186
msgid "Invalid token." msgid "Invalid token."
msgstr "" msgstr ""
...@@ -86,11 +86,12 @@ msgstr "" ...@@ -86,11 +86,12 @@ msgstr ""
msgid "You do not have permission to perform this action." msgid "You do not have permission to perform this action."
msgstr "" msgstr ""
#: exceptions.py:104 views.py:79 #: exceptions.py:104 views.py:81
msgid "Not found." msgid "Not found."
msgstr "" msgstr ""
#: exceptions.py:109 #: exceptions.py:109
#, python-brace-format
msgid "Method \"{method}\" not allowed." msgid "Method \"{method}\" not allowed."
msgstr "" msgstr ""
...@@ -99,6 +100,7 @@ msgid "Could not satisfy the request Accept header." ...@@ -99,6 +100,7 @@ msgid "Could not satisfy the request Accept header."
msgstr "" msgstr ""
#: exceptions.py:132 #: exceptions.py:132
#, python-brace-format
msgid "Unsupported media type \"{media_type}\" in request." msgid "Unsupported media type \"{media_type}\" in request."
msgstr "" msgstr ""
...@@ -106,209 +108,238 @@ msgstr "" ...@@ -106,209 +108,238 @@ msgstr ""
msgid "Request was throttled." msgid "Request was throttled."
msgstr "" msgstr ""
#: fields.py:167 relations.py:173 relations.py:206 validators.py:79 #: fields.py:262 relations.py:191 relations.py:224 validators.py:79
#: validators.py:162 #: validators.py:162
msgid "This field is required." msgid "This field is required."
msgstr "" msgstr ""
#: fields.py:168 #: fields.py:263
msgid "This field may not be null." msgid "This field may not be null."
msgstr "" msgstr ""
#: fields.py:504 fields.py:532 #: fields.py:599 fields.py:627
#, python-brace-format
msgid "\"{input}\" is not a valid boolean." msgid "\"{input}\" is not a valid boolean."
msgstr "" msgstr ""
#: fields.py:567 #: fields.py:662
msgid "This field may not be blank." msgid "This field may not be blank."
msgstr "" msgstr ""
#: fields.py:568 fields.py:1482 #: fields.py:663 fields.py:1594
#, python-brace-format
msgid "Ensure this field has no more than {max_length} characters." msgid "Ensure this field has no more than {max_length} characters."
msgstr "" msgstr ""
#: fields.py:569 #: fields.py:664
#, python-brace-format
msgid "Ensure this field has at least {min_length} characters." msgid "Ensure this field has at least {min_length} characters."
msgstr "" msgstr ""
#: fields.py:606 #: fields.py:701
msgid "Enter a valid email address." msgid "Enter a valid email address."
msgstr "" msgstr ""
#: fields.py:617 #: fields.py:712
msgid "This value does not match the required pattern." msgid "This value does not match the required pattern."
msgstr "" msgstr ""
#: fields.py:628 #: fields.py:723
msgid "" msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or " "Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens." "hyphens."
msgstr "" msgstr ""
#: fields.py:640 #: fields.py:735
msgid "Enter a valid URL." msgid "Enter a valid URL."
msgstr "" msgstr ""
#: fields.py:653 #: fields.py:748
#, python-brace-format
msgid "\"{value}\" is not a valid UUID." msgid "\"{value}\" is not a valid UUID."
msgstr "" msgstr ""
#: fields.py:687 #: fields.py:782
msgid "Enter a valid IPv4 or IPv6 address." msgid "Enter a valid IPv4 or IPv6 address."
msgstr "" msgstr ""
#: fields.py:712 #: fields.py:807
msgid "A valid integer is required." msgid "A valid integer is required."
msgstr "" msgstr ""
#: fields.py:713 fields.py:748 fields.py:781 #: fields.py:808 fields.py:843 fields.py:876
#, python-brace-format
msgid "Ensure this value is less than or equal to {max_value}." msgid "Ensure this value is less than or equal to {max_value}."
msgstr "" msgstr ""
#: fields.py:714 fields.py:749 fields.py:782 #: fields.py:809 fields.py:844 fields.py:877
#, python-brace-format
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 "" msgstr ""
#: fields.py:715 fields.py:750 fields.py:786 #: fields.py:810 fields.py:845 fields.py:881
msgid "String value too large." msgid "String value too large."
msgstr "" msgstr ""
#: fields.py:747 fields.py:780 #: fields.py:842 fields.py:875
msgid "A valid number is required." msgid "A valid number is required."
msgstr "" msgstr ""
#: fields.py:783 #: fields.py:878
#, python-brace-format
msgid "Ensure that there are no more than {max_digits} digits in total." msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "" msgstr ""
#: fields.py:784 #: fields.py:879
#, python-brace-format
msgid "" msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places." "Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "" msgstr ""
#: fields.py:785 #: fields.py:880
#, python-brace-format
msgid "" msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the " "Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point." "decimal point."
msgstr "" msgstr ""
#: fields.py:899 #: fields.py:994
#, python-brace-format
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 ""
#: fields.py:900 #: fields.py:995
msgid "Expected a datetime but got a date." msgid "Expected a datetime but got a date."
msgstr "" msgstr ""
#: fields.py:965 #: fields.py:1060
#, python-brace-format
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 ""
#: fields.py:966 #: fields.py:1061
msgid "Expected a date but got a datetime." msgid "Expected a date but got a datetime."
msgstr "" msgstr ""
#: fields.py:1030 #: fields.py:1125
#, python-brace-format
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 ""
#: fields.py:1085 #: fields.py:1180
#, python-brace-format
msgid "Duration has wrong format. Use one of these formats instead: {format}." msgid "Duration has wrong format. Use one of these formats instead: {format}."
msgstr "" msgstr ""
#: fields.py:1110 fields.py:1154 #: fields.py:1205 fields.py:1254
#, python-brace-format
msgid "\"{input}\" is not a valid choice." msgid "\"{input}\" is not a valid choice."
msgstr "" msgstr ""
#: fields.py:1155 fields.py:1301 relations.py:405 serializers.py:504 #: fields.py:1208 relations.py:58 relations.py:427
#, python-brace-format
msgid "More than {count} items..."
msgstr ""
#: fields.py:1255 fields.py:1401 relations.py:423 serializers.py:504
#, python-brace-format
msgid "Expected a list of items but got type \"{input_type}\"." msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "" msgstr ""
#: fields.py:1156 #: fields.py:1256
msgid "This selection may not be empty." msgid "This selection may not be empty."
msgstr "" msgstr ""
#: fields.py:1194 #: fields.py:1294
#, python-brace-format
msgid "\"{input}\" is not a valid path choice." msgid "\"{input}\" is not a valid path choice."
msgstr "" msgstr ""
#: fields.py:1213 #: fields.py:1313
msgid "No file was submitted." msgid "No file was submitted."
msgstr "" msgstr ""
#: fields.py:1214 #: fields.py:1314
msgid "" msgid ""
"The submitted data was not a file. Check the encoding type on the form." "The submitted data was not a file. Check the encoding type on the form."
msgstr "" msgstr ""
#: fields.py:1215 #: fields.py:1315
msgid "No filename could be determined." msgid "No filename could be determined."
msgstr "" msgstr ""
#: fields.py:1216 #: fields.py:1316
msgid "The submitted file is empty." msgid "The submitted file is empty."
msgstr "" msgstr ""
#: fields.py:1217 #: fields.py:1317
#, python-brace-format
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 ""
#: fields.py:1263 #: fields.py:1363
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 ""
#: fields.py:1302 relations.py:406 serializers.py:505 #: fields.py:1402 relations.py:424 serializers.py:505
msgid "This list may not be empty." msgid "This list may not be empty."
msgstr "" msgstr ""
#: fields.py:1346 #: fields.py:1452
#, python-brace-format
msgid "Expected a dictionary of items but got type \"{input_type}\"." msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "" msgstr ""
#: pagination.py:192 #: pagination.py:192
#, python-brace-format
msgid "Invalid page \"{page_number}\": {message}." msgid "Invalid page \"{page_number}\": {message}."
msgstr "" msgstr ""
#: pagination.py:459 #: pagination.py:462
msgid "Invalid cursor" msgid "Invalid cursor"
msgstr "" msgstr ""
#: relations.py:174 #: relations.py:192
#, python-brace-format
msgid "Invalid pk \"{pk_value}\" - object does not exist." msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "" msgstr ""
#: relations.py:175 #: relations.py:193
#, python-brace-format
msgid "Incorrect type. Expected pk value, received {data_type}." msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "" msgstr ""
#: relations.py:207 #: relations.py:225
msgid "Invalid hyperlink - No URL match." msgid "Invalid hyperlink - No URL match."
msgstr "" msgstr ""
#: relations.py:208 #: relations.py:226
msgid "Invalid hyperlink - Incorrect URL match." msgid "Invalid hyperlink - Incorrect URL match."
msgstr "" msgstr ""
#: relations.py:209 #: relations.py:227
msgid "Invalid hyperlink - Object does not exist." msgid "Invalid hyperlink - Object does not exist."
msgstr "" msgstr ""
#: relations.py:210 #: relations.py:228
#, python-brace-format
msgid "Incorrect type. Expected URL string, received {data_type}." msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "" msgstr ""
#: relations.py:369 #: relations.py:387
#, python-brace-format
msgid "Object with {slug_name}={value} does not exist." msgid "Object with {slug_name}={value} does not exist."
msgstr "" msgstr ""
#: relations.py:370 #: relations.py:388
msgid "Invalid value." msgid "Invalid value."
msgstr "" msgstr ""
#: serializers.py:310 #: serializers.py:310
#, python-brace-format
msgid "Invalid data. Expected a dictionary, but got {datatype}." msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "" msgstr ""
...@@ -329,18 +360,22 @@ msgid "This field must be unique." ...@@ -329,18 +360,22 @@ msgid "This field must be unique."
msgstr "" msgstr ""
#: validators.py:78 #: validators.py:78
#, python-brace-format
msgid "The fields {field_names} must make a unique set." msgid "The fields {field_names} must make a unique set."
msgstr "" msgstr ""
#: validators.py:226 #: validators.py:226
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" date." msgid "This field must be unique for the \"{date_field}\" date."
msgstr "" msgstr ""
#: validators.py:241 #: validators.py:241
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" month." msgid "This field must be unique for the \"{date_field}\" month."
msgstr "" msgstr ""
#: validators.py:254 #: validators.py:254
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" year." msgid "This field must be unique for the \"{date_field}\" year."
msgstr "" msgstr ""
...@@ -360,6 +395,6 @@ msgstr "" ...@@ -360,6 +395,6 @@ msgstr ""
msgid "Invalid version in query parameter." msgid "Invalid version in query parameter."
msgstr "" msgstr ""
#: views.py:86 #: views.py:88
msgid "Permission denied." msgid "Permission denied."
msgstr "" msgstr ""
...@@ -8,9 +8,9 @@ msgid "" ...@@ -8,9 +8,9 @@ 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-08-06 13:21+0100\n" "POT-Creation-Date: 2015-09-21 10:55+0200\n"
"PO-Revision-Date: 2015-08-06 12:21+0000\n" "PO-Revision-Date: 2015-09-21 08:56+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n" "Last-Translator: Xavier Ordoquy <xordoquy@linovia.com>\n"
"Language-Team: Galician (Spain) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/gl_ES/)\n" "Language-Team: Galician (Spain) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/gl_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"
...@@ -18,40 +18,40 @@ msgstr "" ...@@ -18,40 +18,40 @@ msgstr ""
"Language: gl_ES\n" "Language: gl_ES\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: authentication.py:73 #: authentication.py:72
msgid "Invalid basic header. No credentials provided." msgid "Invalid basic header. No credentials provided."
msgstr "" msgstr ""
#: authentication.py:76 #: authentication.py:75
msgid "Invalid basic header. Credentials string should not contain spaces." msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "" msgstr ""
#: authentication.py:82 #: authentication.py:81
msgid "Invalid basic header. Credentials not correctly base64 encoded." msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "" msgstr ""
#: authentication.py:100 #: authentication.py:98
msgid "Invalid username/password." msgid "Invalid username/password."
msgstr "" msgstr ""
#: authentication.py:103 authentication.py:191 #: authentication.py:101 authentication.py:189
msgid "User inactive or deleted." msgid "User inactive or deleted."
msgstr "" msgstr ""
#: authentication.py:170 #: authentication.py:168
msgid "Invalid token header. No credentials provided." msgid "Invalid token header. No credentials provided."
msgstr "" msgstr ""
#: authentication.py:173 #: authentication.py:171
msgid "Invalid token header. Token string should not contain spaces." msgid "Invalid token header. Token string should not contain spaces."
msgstr "" msgstr ""
#: authentication.py:179 #: authentication.py:177
msgid "" msgid ""
"Invalid token header. Token string should not contain invalid characters." "Invalid token header. Token string should not contain invalid characters."
msgstr "" msgstr ""
#: authentication.py:188 #: authentication.py:186
msgid "Invalid token." msgid "Invalid token."
msgstr "" msgstr ""
...@@ -87,11 +87,12 @@ msgstr "" ...@@ -87,11 +87,12 @@ msgstr ""
msgid "You do not have permission to perform this action." msgid "You do not have permission to perform this action."
msgstr "" msgstr ""
#: exceptions.py:104 views.py:79 #: exceptions.py:104 views.py:81
msgid "Not found." msgid "Not found."
msgstr "" msgstr ""
#: exceptions.py:109 #: exceptions.py:109
#, python-brace-format
msgid "Method \"{method}\" not allowed." msgid "Method \"{method}\" not allowed."
msgstr "" msgstr ""
...@@ -100,6 +101,7 @@ msgid "Could not satisfy the request Accept header." ...@@ -100,6 +101,7 @@ msgid "Could not satisfy the request Accept header."
msgstr "" msgstr ""
#: exceptions.py:132 #: exceptions.py:132
#, python-brace-format
msgid "Unsupported media type \"{media_type}\" in request." msgid "Unsupported media type \"{media_type}\" in request."
msgstr "" msgstr ""
...@@ -107,209 +109,238 @@ msgstr "" ...@@ -107,209 +109,238 @@ msgstr ""
msgid "Request was throttled." msgid "Request was throttled."
msgstr "" msgstr ""
#: fields.py:167 relations.py:173 relations.py:206 validators.py:79 #: fields.py:262 relations.py:191 relations.py:224 validators.py:79
#: validators.py:162 #: validators.py:162
msgid "This field is required." msgid "This field is required."
msgstr "" msgstr ""
#: fields.py:168 #: fields.py:263
msgid "This field may not be null." msgid "This field may not be null."
msgstr "" msgstr ""
#: fields.py:504 fields.py:532 #: fields.py:599 fields.py:627
#, python-brace-format
msgid "\"{input}\" is not a valid boolean." msgid "\"{input}\" is not a valid boolean."
msgstr "" msgstr ""
#: fields.py:567 #: fields.py:662
msgid "This field may not be blank." msgid "This field may not be blank."
msgstr "" msgstr ""
#: fields.py:568 fields.py:1482 #: fields.py:663 fields.py:1594
#, python-brace-format
msgid "Ensure this field has no more than {max_length} characters." msgid "Ensure this field has no more than {max_length} characters."
msgstr "" msgstr ""
#: fields.py:569 #: fields.py:664
#, python-brace-format
msgid "Ensure this field has at least {min_length} characters." msgid "Ensure this field has at least {min_length} characters."
msgstr "" msgstr ""
#: fields.py:606 #: fields.py:701
msgid "Enter a valid email address." msgid "Enter a valid email address."
msgstr "" msgstr ""
#: fields.py:617 #: fields.py:712
msgid "This value does not match the required pattern." msgid "This value does not match the required pattern."
msgstr "" msgstr ""
#: fields.py:628 #: fields.py:723
msgid "" msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or " "Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens." "hyphens."
msgstr "" msgstr ""
#: fields.py:640 #: fields.py:735
msgid "Enter a valid URL." msgid "Enter a valid URL."
msgstr "" msgstr ""
#: fields.py:653 #: fields.py:748
#, python-brace-format
msgid "\"{value}\" is not a valid UUID." msgid "\"{value}\" is not a valid UUID."
msgstr "" msgstr ""
#: fields.py:687 #: fields.py:782
msgid "Enter a valid IPv4 or IPv6 address." msgid "Enter a valid IPv4 or IPv6 address."
msgstr "" msgstr ""
#: fields.py:712 #: fields.py:807
msgid "A valid integer is required." msgid "A valid integer is required."
msgstr "" msgstr ""
#: fields.py:713 fields.py:748 fields.py:781 #: fields.py:808 fields.py:843 fields.py:876
#, python-brace-format
msgid "Ensure this value is less than or equal to {max_value}." msgid "Ensure this value is less than or equal to {max_value}."
msgstr "" msgstr ""
#: fields.py:714 fields.py:749 fields.py:782 #: fields.py:809 fields.py:844 fields.py:877
#, python-brace-format
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 "" msgstr ""
#: fields.py:715 fields.py:750 fields.py:786 #: fields.py:810 fields.py:845 fields.py:881
msgid "String value too large." msgid "String value too large."
msgstr "" msgstr ""
#: fields.py:747 fields.py:780 #: fields.py:842 fields.py:875
msgid "A valid number is required." msgid "A valid number is required."
msgstr "" msgstr ""
#: fields.py:783 #: fields.py:878
#, python-brace-format
msgid "Ensure that there are no more than {max_digits} digits in total." msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "" msgstr ""
#: fields.py:784 #: fields.py:879
#, python-brace-format
msgid "" msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places." "Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "" msgstr ""
#: fields.py:785 #: fields.py:880
#, python-brace-format
msgid "" msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the " "Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point." "decimal point."
msgstr "" msgstr ""
#: fields.py:899 #: fields.py:994
#, python-brace-format
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 ""
#: fields.py:900 #: fields.py:995
msgid "Expected a datetime but got a date." msgid "Expected a datetime but got a date."
msgstr "" msgstr ""
#: fields.py:965 #: fields.py:1060
#, python-brace-format
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 ""
#: fields.py:966 #: fields.py:1061
msgid "Expected a date but got a datetime." msgid "Expected a date but got a datetime."
msgstr "" msgstr ""
#: fields.py:1030 #: fields.py:1125
#, python-brace-format
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 ""
#: fields.py:1085 #: fields.py:1180
#, python-brace-format
msgid "Duration has wrong format. Use one of these formats instead: {format}." msgid "Duration has wrong format. Use one of these formats instead: {format}."
msgstr "" msgstr ""
#: fields.py:1110 fields.py:1154 #: fields.py:1205 fields.py:1254
#, python-brace-format
msgid "\"{input}\" is not a valid choice." msgid "\"{input}\" is not a valid choice."
msgstr "" msgstr ""
#: fields.py:1155 fields.py:1301 relations.py:405 serializers.py:504 #: fields.py:1208 relations.py:58 relations.py:427
#, python-brace-format
msgid "More than {count} items..."
msgstr ""
#: fields.py:1255 fields.py:1401 relations.py:423 serializers.py:504
#, python-brace-format
msgid "Expected a list of items but got type \"{input_type}\"." msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "" msgstr ""
#: fields.py:1156 #: fields.py:1256
msgid "This selection may not be empty." msgid "This selection may not be empty."
msgstr "" msgstr ""
#: fields.py:1194 #: fields.py:1294
#, python-brace-format
msgid "\"{input}\" is not a valid path choice." msgid "\"{input}\" is not a valid path choice."
msgstr "" msgstr ""
#: fields.py:1213 #: fields.py:1313
msgid "No file was submitted." msgid "No file was submitted."
msgstr "" msgstr ""
#: fields.py:1214 #: fields.py:1314
msgid "" msgid ""
"The submitted data was not a file. Check the encoding type on the form." "The submitted data was not a file. Check the encoding type on the form."
msgstr "" msgstr ""
#: fields.py:1215 #: fields.py:1315
msgid "No filename could be determined." msgid "No filename could be determined."
msgstr "" msgstr ""
#: fields.py:1216 #: fields.py:1316
msgid "The submitted file is empty." msgid "The submitted file is empty."
msgstr "" msgstr ""
#: fields.py:1217 #: fields.py:1317
#, python-brace-format
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 ""
#: fields.py:1263 #: fields.py:1363
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 ""
#: fields.py:1302 relations.py:406 serializers.py:505 #: fields.py:1402 relations.py:424 serializers.py:505
msgid "This list may not be empty." msgid "This list may not be empty."
msgstr "" msgstr ""
#: fields.py:1346 #: fields.py:1452
#, python-brace-format
msgid "Expected a dictionary of items but got type \"{input_type}\"." msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "" msgstr ""
#: pagination.py:192 #: pagination.py:192
#, python-brace-format
msgid "Invalid page \"{page_number}\": {message}." msgid "Invalid page \"{page_number}\": {message}."
msgstr "" msgstr ""
#: pagination.py:459 #: pagination.py:462
msgid "Invalid cursor" msgid "Invalid cursor"
msgstr "" msgstr ""
#: relations.py:174 #: relations.py:192
#, python-brace-format
msgid "Invalid pk \"{pk_value}\" - object does not exist." msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "" msgstr ""
#: relations.py:175 #: relations.py:193
#, python-brace-format
msgid "Incorrect type. Expected pk value, received {data_type}." msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "" msgstr ""
#: relations.py:207 #: relations.py:225
msgid "Invalid hyperlink - No URL match." msgid "Invalid hyperlink - No URL match."
msgstr "" msgstr ""
#: relations.py:208 #: relations.py:226
msgid "Invalid hyperlink - Incorrect URL match." msgid "Invalid hyperlink - Incorrect URL match."
msgstr "" msgstr ""
#: relations.py:209 #: relations.py:227
msgid "Invalid hyperlink - Object does not exist." msgid "Invalid hyperlink - Object does not exist."
msgstr "" msgstr ""
#: relations.py:210 #: relations.py:228
#, python-brace-format
msgid "Incorrect type. Expected URL string, received {data_type}." msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "" msgstr ""
#: relations.py:369 #: relations.py:387
#, python-brace-format
msgid "Object with {slug_name}={value} does not exist." msgid "Object with {slug_name}={value} does not exist."
msgstr "" msgstr ""
#: relations.py:370 #: relations.py:388
msgid "Invalid value." msgid "Invalid value."
msgstr "Valor non válido." msgstr "Valor non válido."
#: serializers.py:310 #: serializers.py:310
#, python-brace-format
msgid "Invalid data. Expected a dictionary, but got {datatype}." msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "" msgstr ""
...@@ -330,18 +361,22 @@ msgid "This field must be unique." ...@@ -330,18 +361,22 @@ msgid "This field must be unique."
msgstr "" msgstr ""
#: validators.py:78 #: validators.py:78
#, python-brace-format
msgid "The fields {field_names} must make a unique set." msgid "The fields {field_names} must make a unique set."
msgstr "" msgstr ""
#: validators.py:226 #: validators.py:226
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" date." msgid "This field must be unique for the \"{date_field}\" date."
msgstr "" msgstr ""
#: validators.py:241 #: validators.py:241
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" month." msgid "This field must be unique for the \"{date_field}\" month."
msgstr "" msgstr ""
#: validators.py:254 #: validators.py:254
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" year." msgid "This field must be unique for the \"{date_field}\" year."
msgstr "" msgstr ""
...@@ -361,6 +396,6 @@ msgstr "" ...@@ -361,6 +396,6 @@ msgstr ""
msgid "Invalid version in query parameter." msgid "Invalid version in query parameter."
msgstr "" msgstr ""
#: views.py:86 #: views.py:88
msgid "Permission denied." msgid "Permission denied."
msgstr "Permiso denegado." msgstr "Permiso denegado."
...@@ -7,9 +7,9 @@ msgid "" ...@@ -7,9 +7,9 @@ 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-08-06 13:21+0100\n" "POT-Creation-Date: 2015-09-21 10:55+0200\n"
"PO-Revision-Date: 2015-08-06 12:21+0000\n" "PO-Revision-Date: 2015-09-21 08:56+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n" "Last-Translator: Xavier Ordoquy <xordoquy@linovia.com>\n"
"Language-Team: Indonesian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/id/)\n" "Language-Team: Indonesian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/id/)\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"
...@@ -17,40 +17,40 @@ msgstr "" ...@@ -17,40 +17,40 @@ msgstr ""
"Language: id\n" "Language: id\n"
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
#: authentication.py:73 #: authentication.py:72
msgid "Invalid basic header. No credentials provided." msgid "Invalid basic header. No credentials provided."
msgstr "" msgstr ""
#: authentication.py:76 #: authentication.py:75
msgid "Invalid basic header. Credentials string should not contain spaces." msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "" msgstr ""
#: authentication.py:82 #: authentication.py:81
msgid "Invalid basic header. Credentials not correctly base64 encoded." msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "" msgstr ""
#: authentication.py:100 #: authentication.py:98
msgid "Invalid username/password." msgid "Invalid username/password."
msgstr "" msgstr ""
#: authentication.py:103 authentication.py:191 #: authentication.py:101 authentication.py:189
msgid "User inactive or deleted." msgid "User inactive or deleted."
msgstr "" msgstr ""
#: authentication.py:170 #: authentication.py:168
msgid "Invalid token header. No credentials provided." msgid "Invalid token header. No credentials provided."
msgstr "" msgstr ""
#: authentication.py:173 #: authentication.py:171
msgid "Invalid token header. Token string should not contain spaces." msgid "Invalid token header. Token string should not contain spaces."
msgstr "" msgstr ""
#: authentication.py:179 #: authentication.py:177
msgid "" msgid ""
"Invalid token header. Token string should not contain invalid characters." "Invalid token header. Token string should not contain invalid characters."
msgstr "" msgstr ""
#: authentication.py:188 #: authentication.py:186
msgid "Invalid token." msgid "Invalid token."
msgstr "" msgstr ""
...@@ -86,11 +86,12 @@ msgstr "" ...@@ -86,11 +86,12 @@ msgstr ""
msgid "You do not have permission to perform this action." msgid "You do not have permission to perform this action."
msgstr "" msgstr ""
#: exceptions.py:104 views.py:79 #: exceptions.py:104 views.py:81
msgid "Not found." msgid "Not found."
msgstr "" msgstr ""
#: exceptions.py:109 #: exceptions.py:109
#, python-brace-format
msgid "Method \"{method}\" not allowed." msgid "Method \"{method}\" not allowed."
msgstr "" msgstr ""
...@@ -99,6 +100,7 @@ msgid "Could not satisfy the request Accept header." ...@@ -99,6 +100,7 @@ msgid "Could not satisfy the request Accept header."
msgstr "" msgstr ""
#: exceptions.py:132 #: exceptions.py:132
#, python-brace-format
msgid "Unsupported media type \"{media_type}\" in request." msgid "Unsupported media type \"{media_type}\" in request."
msgstr "" msgstr ""
...@@ -106,209 +108,238 @@ msgstr "" ...@@ -106,209 +108,238 @@ msgstr ""
msgid "Request was throttled." msgid "Request was throttled."
msgstr "" msgstr ""
#: fields.py:167 relations.py:173 relations.py:206 validators.py:79 #: fields.py:262 relations.py:191 relations.py:224 validators.py:79
#: validators.py:162 #: validators.py:162
msgid "This field is required." msgid "This field is required."
msgstr "" msgstr ""
#: fields.py:168 #: fields.py:263
msgid "This field may not be null." msgid "This field may not be null."
msgstr "" msgstr ""
#: fields.py:504 fields.py:532 #: fields.py:599 fields.py:627
#, python-brace-format
msgid "\"{input}\" is not a valid boolean." msgid "\"{input}\" is not a valid boolean."
msgstr "" msgstr ""
#: fields.py:567 #: fields.py:662
msgid "This field may not be blank." msgid "This field may not be blank."
msgstr "" msgstr ""
#: fields.py:568 fields.py:1482 #: fields.py:663 fields.py:1594
#, python-brace-format
msgid "Ensure this field has no more than {max_length} characters." msgid "Ensure this field has no more than {max_length} characters."
msgstr "" msgstr ""
#: fields.py:569 #: fields.py:664
#, python-brace-format
msgid "Ensure this field has at least {min_length} characters." msgid "Ensure this field has at least {min_length} characters."
msgstr "" msgstr ""
#: fields.py:606 #: fields.py:701
msgid "Enter a valid email address." msgid "Enter a valid email address."
msgstr "" msgstr ""
#: fields.py:617 #: fields.py:712
msgid "This value does not match the required pattern." msgid "This value does not match the required pattern."
msgstr "" msgstr ""
#: fields.py:628 #: fields.py:723
msgid "" msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or " "Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens." "hyphens."
msgstr "" msgstr ""
#: fields.py:640 #: fields.py:735
msgid "Enter a valid URL." msgid "Enter a valid URL."
msgstr "" msgstr ""
#: fields.py:653 #: fields.py:748
#, python-brace-format
msgid "\"{value}\" is not a valid UUID." msgid "\"{value}\" is not a valid UUID."
msgstr "" msgstr ""
#: fields.py:687 #: fields.py:782
msgid "Enter a valid IPv4 or IPv6 address." msgid "Enter a valid IPv4 or IPv6 address."
msgstr "" msgstr ""
#: fields.py:712 #: fields.py:807
msgid "A valid integer is required." msgid "A valid integer is required."
msgstr "" msgstr ""
#: fields.py:713 fields.py:748 fields.py:781 #: fields.py:808 fields.py:843 fields.py:876
#, python-brace-format
msgid "Ensure this value is less than or equal to {max_value}." msgid "Ensure this value is less than or equal to {max_value}."
msgstr "" msgstr ""
#: fields.py:714 fields.py:749 fields.py:782 #: fields.py:809 fields.py:844 fields.py:877
#, python-brace-format
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 "" msgstr ""
#: fields.py:715 fields.py:750 fields.py:786 #: fields.py:810 fields.py:845 fields.py:881
msgid "String value too large." msgid "String value too large."
msgstr "" msgstr ""
#: fields.py:747 fields.py:780 #: fields.py:842 fields.py:875
msgid "A valid number is required." msgid "A valid number is required."
msgstr "" msgstr ""
#: fields.py:783 #: fields.py:878
#, python-brace-format
msgid "Ensure that there are no more than {max_digits} digits in total." msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "" msgstr ""
#: fields.py:784 #: fields.py:879
#, python-brace-format
msgid "" msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places." "Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "" msgstr ""
#: fields.py:785 #: fields.py:880
#, python-brace-format
msgid "" msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the " "Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point." "decimal point."
msgstr "" msgstr ""
#: fields.py:899 #: fields.py:994
#, python-brace-format
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 ""
#: fields.py:900 #: fields.py:995
msgid "Expected a datetime but got a date." msgid "Expected a datetime but got a date."
msgstr "" msgstr ""
#: fields.py:965 #: fields.py:1060
#, python-brace-format
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 ""
#: fields.py:966 #: fields.py:1061
msgid "Expected a date but got a datetime." msgid "Expected a date but got a datetime."
msgstr "" msgstr ""
#: fields.py:1030 #: fields.py:1125
#, python-brace-format
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 ""
#: fields.py:1085 #: fields.py:1180
#, python-brace-format
msgid "Duration has wrong format. Use one of these formats instead: {format}." msgid "Duration has wrong format. Use one of these formats instead: {format}."
msgstr "" msgstr ""
#: fields.py:1110 fields.py:1154 #: fields.py:1205 fields.py:1254
#, python-brace-format
msgid "\"{input}\" is not a valid choice." msgid "\"{input}\" is not a valid choice."
msgstr "" msgstr ""
#: fields.py:1155 fields.py:1301 relations.py:405 serializers.py:504 #: fields.py:1208 relations.py:58 relations.py:427
#, python-brace-format
msgid "More than {count} items..."
msgstr ""
#: fields.py:1255 fields.py:1401 relations.py:423 serializers.py:504
#, python-brace-format
msgid "Expected a list of items but got type \"{input_type}\"." msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "" msgstr ""
#: fields.py:1156 #: fields.py:1256
msgid "This selection may not be empty." msgid "This selection may not be empty."
msgstr "" msgstr ""
#: fields.py:1194 #: fields.py:1294
#, python-brace-format
msgid "\"{input}\" is not a valid path choice." msgid "\"{input}\" is not a valid path choice."
msgstr "" msgstr ""
#: fields.py:1213 #: fields.py:1313
msgid "No file was submitted." msgid "No file was submitted."
msgstr "" msgstr ""
#: fields.py:1214 #: fields.py:1314
msgid "" msgid ""
"The submitted data was not a file. Check the encoding type on the form." "The submitted data was not a file. Check the encoding type on the form."
msgstr "" msgstr ""
#: fields.py:1215 #: fields.py:1315
msgid "No filename could be determined." msgid "No filename could be determined."
msgstr "" msgstr ""
#: fields.py:1216 #: fields.py:1316
msgid "The submitted file is empty." msgid "The submitted file is empty."
msgstr "" msgstr ""
#: fields.py:1217 #: fields.py:1317
#, python-brace-format
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 ""
#: fields.py:1263 #: fields.py:1363
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 ""
#: fields.py:1302 relations.py:406 serializers.py:505 #: fields.py:1402 relations.py:424 serializers.py:505
msgid "This list may not be empty." msgid "This list may not be empty."
msgstr "" msgstr ""
#: fields.py:1346 #: fields.py:1452
#, python-brace-format
msgid "Expected a dictionary of items but got type \"{input_type}\"." msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr "" msgstr ""
#: pagination.py:192 #: pagination.py:192
#, python-brace-format
msgid "Invalid page \"{page_number}\": {message}." msgid "Invalid page \"{page_number}\": {message}."
msgstr "" msgstr ""
#: pagination.py:459 #: pagination.py:462
msgid "Invalid cursor" msgid "Invalid cursor"
msgstr "" msgstr ""
#: relations.py:174 #: relations.py:192
#, python-brace-format
msgid "Invalid pk \"{pk_value}\" - object does not exist." msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "" msgstr ""
#: relations.py:175 #: relations.py:193
#, python-brace-format
msgid "Incorrect type. Expected pk value, received {data_type}." msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "" msgstr ""
#: relations.py:207 #: relations.py:225
msgid "Invalid hyperlink - No URL match." msgid "Invalid hyperlink - No URL match."
msgstr "" msgstr ""
#: relations.py:208 #: relations.py:226
msgid "Invalid hyperlink - Incorrect URL match." msgid "Invalid hyperlink - Incorrect URL match."
msgstr "" msgstr ""
#: relations.py:209 #: relations.py:227
msgid "Invalid hyperlink - Object does not exist." msgid "Invalid hyperlink - Object does not exist."
msgstr "" msgstr ""
#: relations.py:210 #: relations.py:228
#, python-brace-format
msgid "Incorrect type. Expected URL string, received {data_type}." msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "" msgstr ""
#: relations.py:369 #: relations.py:387
#, python-brace-format
msgid "Object with {slug_name}={value} does not exist." msgid "Object with {slug_name}={value} does not exist."
msgstr "" msgstr ""
#: relations.py:370 #: relations.py:388
msgid "Invalid value." msgid "Invalid value."
msgstr "" msgstr ""
#: serializers.py:310 #: serializers.py:310
#, python-brace-format
msgid "Invalid data. Expected a dictionary, but got {datatype}." msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "" msgstr ""
...@@ -329,18 +360,22 @@ msgid "This field must be unique." ...@@ -329,18 +360,22 @@ msgid "This field must be unique."
msgstr "" msgstr ""
#: validators.py:78 #: validators.py:78
#, python-brace-format
msgid "The fields {field_names} must make a unique set." msgid "The fields {field_names} must make a unique set."
msgstr "" msgstr ""
#: validators.py:226 #: validators.py:226
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" date." msgid "This field must be unique for the \"{date_field}\" date."
msgstr "" msgstr ""
#: validators.py:241 #: validators.py:241
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" month." msgid "This field must be unique for the \"{date_field}\" month."
msgstr "" msgstr ""
#: validators.py:254 #: validators.py:254
#, python-brace-format
msgid "This field must be unique for the \"{date_field}\" year." msgid "This field must be unique for the \"{date_field}\" year."
msgstr "" msgstr ""
...@@ -360,6 +395,6 @@ msgstr "" ...@@ -360,6 +395,6 @@ msgstr ""
msgid "Invalid version in query parameter." msgid "Invalid version in query parameter."
msgstr "" msgstr ""
#: views.py:86 #: views.py:88
msgid "Permission denied." msgid "Permission denied."
msgstr "" msgstr ""
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