Merge pull request #2198 from tomchristie/version-3.1

Version 3.1
This commit is contained in:
Tom Christie 2015-02-13 13:38:44 +00:00
commit 4248a8d3fc
118 changed files with 11323 additions and 3905 deletions

9
.tx/config Normal file
View File

@ -0,0 +1,9 @@
[main]
host = https://www.transifex.com
[django-rest-framework.djangopo]
file_filter = rest_framework/locale/<lang>/LC_MESSAGES/django.po
source_file = rest_framework/locale/en_US/LC_MESSAGES/django.po
source_lang = en_US
type = PO

View File

@ -10,9 +10,9 @@ There are many ways you can contribute to Django REST framework. We'd like it t
The most important thing you can do to help push the REST framework project forward is to be actively involved wherever possible. Code contributions are often overvalued as being the primary way to get involved in a project, we don't believe that needs to be the case.
If you use REST framework, we'd love you to be vocal about your experiences with it - you might consider writing a blog post about using REST framework, or publishing a tutorial about building a project with a particular Javascript framework. Experiences from beginners can be particularly helpful because you'll be in the best position to assess which bits of REST framework are more difficult to understand and work with.
If you use REST framework, we'd love you to be vocal about your experiences with it - you might consider writing a blog post about using REST framework, or publishing a tutorial about building a project with a particular JavaScript framework. Experiences from beginners can be particularly helpful because you'll be in the best position to assess which bits of REST framework are more difficult to understand and work with.
Other really great ways you can help move the community forward include helping answer questions on the [discussion group][google-group], or setting up an [email alert on StackOverflow][so-filter] so that you get notified of any new questions with the `django-rest-framework` tag.
Other really great ways you can help move the community forward include helping to answer questions on the [discussion group][google-group], or setting up an [email alert on StackOverflow][so-filter] so that you get notified of any new questions with the `django-rest-framework` tag.
When answering questions make sure to help future contributors find their way around by hyperlinking wherever possible to related threads and tickets, and include backlinks from those items if relevant.
@ -52,7 +52,7 @@ To start developing on Django REST framework, clone the repo:
git clone git@github.com:tomchristie/django-rest-framework.git
Changes should broadly follow the [PEP 8][pep-8] style conventions, and we recommend you setup your editor to automatically indicated non-conforming styles.
Changes should broadly follow the [PEP 8][pep-8] style conventions, and we recommend you set up your editor to automatically indicate non-conforming styles.
## Testing
@ -60,13 +60,47 @@ To run the tests, clone the repository, and then:
# Setup the virtual environment
virtualenv env
env/bin/activate
source env/bin/activate
pip install -r requirements.txt
# Run the tests
./runtests.py
You can also use the excellent [`tox`][tox] testing tool to run the tests against all supported versions of Python and Django. Install `tox` globally, and then simply run:
### Test options
Run using a more concise output style.
./runtests.py -q
Run the tests using a more concise output style, no coverage, no flake8.
./runtests.py --fast
Don't run the flake8 code linting.
./runtests.py --nolint
Only run the flake8 code linting, don't run the tests.
./runtests.py --lintonly
Run the tests for a given test case.
./runtests.py MyTestCase
Run the tests for a given test method.
./runtests.py MyTestCase.test_this_method
Shorter form to run the tests for a given test method.
./runtests.py test_this_method
Note: The test case and test method matching is fuzzy and will sometimes run other tests that contain a partial string match to the given command line input.
### Running against multiple environments
You can also use the excellent [tox][tox] testing tool to run the tests against all supported versions of Python and Django. Install `tox` globally, and then simply run:
tox
@ -82,7 +116,7 @@ GitHub's documentation for working on pull requests is [available here][pull-req
Always run the tests before submitting pull requests, and ideally run `tox` in order to check that your modifications are compatible with both Python 2 and Python 3, and that they run properly on all supported versions of Django.
Once you've made a pull request take a look at the travis build status in the GitHub interface and make sure the tests are running as you'd expect.
Once you've made a pull request take a look at the Travis build status in the GitHub interface and make sure the tests are running as you'd expect.
![Travis status][travis-status]
@ -96,7 +130,7 @@ Sometimes, in order to ensure your code works on various different versions of D
The documentation for REST framework is built from the [Markdown][markdown] source files in [the docs directory][docs].
There are many great markdown editors that make working with the documentation really easy. The [Mou editor for Mac][mou] is one such editor that comes highly recommended.
There are many great Markdown editors that make working with the documentation really easy. The [Mou editor for Mac][mou] is one such editor that comes highly recommended.
## Building the documentation
@ -104,7 +138,7 @@ To build the documentation, install MkDocs with `pip install mkdocs` and then ru
mkdocs build
This will build the html output into the `html` directory.
This will build the documentation into the `site` directory.
You can build the documentation and open a preview in a browser window by using the `serve` command.
@ -117,8 +151,7 @@ Documentation should be in American English. The tone of the documentation is v
Some other tips:
* Keep paragraphs reasonably short.
* Use double spacing after the end of sentences.
* Don't use the abbreviations such as 'e.g.' but instead use long form, such as 'For example'.
* Don't use abbreviations such as 'e.g.' but instead use the long form, such as 'For example'.
## Markdown style
@ -151,7 +184,7 @@ If you are hyperlinking to another REST framework document, you should use a rel
[authentication]: ../api-guide/authentication.md
Linking in this style means you'll be able to click the hyperlink in your markdown editor to open the referenced document. When the documentation is built, these links will be converted into regular links to HTML pages.
Linking in this style means you'll be able to click the hyperlink in your Markdown editor to open the referenced document. When the documentation is built, these links will be converted into regular links to HTML pages.
##### 3. Notes
@ -163,19 +196,6 @@ If you want to draw attention to a note or warning, use a pair of enclosing line
---
# Third party packages
New features to REST framework are generally recommended to be implemented as third party libraries that are developed outside of the core framework. Ideally third party libraries should be properly documented and packaged, and made available on PyPI.
## Getting started
If you have some functionality that you would like to implement as a third party package it's worth contacting the [discussion group][google-group] as others may be willing to get involved. We strongly encourage third party package development and will always try to prioritize time spent helping their development, documentation and packaging.
We recommend the [`django-reusable-app`][django-reusable-app] template as a good resource for getting up and running with implementing a third party Django package.
## Linking to your package
Once your package is decently documented and available on PyPI open a pull request or issue, and we'll add a link to it from the main REST framework documentation.
[cite]: http://www.w3.org/People/Berners-Lee/FAQ.html
[code-of-conduct]: https://www.djangoproject.com/conduct/
@ -183,10 +203,9 @@ Once your package is decently documented and available on PyPI open a pull reque
[so-filter]: http://stackexchange.com/filters/66475/rest-framework
[issues]: https://github.com/tomchristie/django-rest-framework/issues?state=open
[pep-8]: http://www.python.org/dev/peps/pep-0008/
[travis-status]: https://raw.github.com/tomchristie/django-rest-framework/master/docs/img/travis-status.png
[travis-status]: ../img/travis-status.png
[pull-requests]: https://help.github.com/articles/using-pull-requests
[tox]: http://tox.readthedocs.org/en/latest/
[markdown]: http://daringfireball.net/projects/markdown/basics
[docs]: https://github.com/tomchristie/django-rest-framework/tree/master/docs
[mou]: http://mouapp.com/
[django-reusable-app]: https://github.com/dabapps/django-reusable-app

View File

@ -20,7 +20,7 @@ Django REST framework is a powerful and flexible toolkit for building Web APIs.
Some reasons you might want to use REST framework:
* The [Web browsable API][sandbox] is a huge usability win for your developers.
* [Authentication policies][authentication] including [OAuth1a][oauth1-section] and [OAuth2][oauth2-section] out of the box.
* [Authentication policies][authentication] including optional packages for [OAuth1a][oauth1-section] and [OAuth2][oauth2-section].
* [Serialization][serializers] that supports both [ORM][modelserializer-section] and [non-ORM][serializer-section] data sources.
* Customizable all the way down - just use [regular function-based views][functionview-section] if you don't need the [more][generic-views] [powerful][viewsets] [features][routers].
* [Extensive documentation][index], and [great community support][group].
@ -190,8 +190,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[sandbox]: http://restframework.herokuapp.com/
[index]: http://www.django-rest-framework.org/
[oauth1-section]: http://www.django-rest-framework.org/api-guide/authentication/#oauthauthentication
[oauth2-section]: http://www.django-rest-framework.org/api-guide/authentication/#oauth2authentication
[oauth1-section]: http://www.django-rest-framework.org/api-guide/authentication/#django-rest-framework-oauth
[oauth2-section]: http://www.django-rest-framework.org/api-guide/authentication/#django-oauth-toolkit
[serializer-section]: http://www.django-rest-framework.org/api-guide/serializers/#serializers
[modelserializer-section]: http://www.django-rest-framework.org/api-guide/serializers/#modelserializer
[functionview-section]: http://www.django-rest-framework.org/api-guide/views/#function-based-views
@ -200,8 +200,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[routers]: http://www.django-rest-framework.org/api-guide/routers/
[serializers]: http://www.django-rest-framework.org/api-guide/serializers/
[authentication]: http://www.django-rest-framework.org/api-guide/authentication/
[rest-framework-2-announcement]: http://www.django-rest-framework.org/topics/rest-framework-2-announcement
[rest-framework-2-announcement]: http://www.django-rest-framework.org/topics/rest-framework-2-announcement/
[2.1.0-notes]: https://groups.google.com/d/topic/django-rest-framework/Vv2M0CMY9bg/discussion
[image]: http://www.django-rest-framework.org/img/quickstart.png
@ -214,7 +213,5 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[docs]: http://www.django-rest-framework.org/
[urlobject]: https://github.com/zacharyvoase/urlobject
[markdown]: http://pypi.python.org/pypi/Markdown/
[pyyaml]: http://pypi.python.org/pypi/PyYAML
[defusedxml]: https://pypi.python.org/pypi/defusedxml
[django-filter]: http://pypi.python.org/pypi/django-filter
[security-mail]: mailto:rest-framework-security@googlegroups.com

View File

@ -126,7 +126,6 @@ To use the `TokenAuthentication` scheme you'll need to [configure the authentica
'rest_framework.authtoken'
)
---
**Note:** Make sure to run `manage.py syncdb` after changing your settings. The `rest_framework.authtoken` app provides both Django (from v1.7) and South database migrations. See [Schema migrations](#schema-migrations) below.
@ -249,107 +248,6 @@ Unauthenticated responses that are denied permission will result in an `HTTP 403
If you're using an AJAX style API with SessionAuthentication, you'll need to make sure you include a valid CSRF token for any "unsafe" HTTP method calls, such as `PUT`, `PATCH`, `POST` or `DELETE` requests. See the [Django CSRF documentation][csrf-ajax] for more details.
## OAuthAuthentication
This authentication uses [OAuth 1.0a][oauth-1.0a] authentication scheme. OAuth 1.0a provides signature validation which provides a reasonable level of security over plain non-HTTPS connections. However, it may also be considered more complicated than OAuth2, as it requires clients to sign their requests.
This authentication class depends on the optional `django-oauth-plus` and `oauth2` packages. In order to make it work you must install these packages and add `oauth_provider` to your `INSTALLED_APPS`:
INSTALLED_APPS = (
...
`oauth_provider`,
)
Don't forget to run `syncdb` once you've added the package.
python manage.py syncdb
#### Getting started with django-oauth-plus
The OAuthAuthentication class only provides token verification and signature validation for requests. It doesn't provide authorization flow for your clients. You still need to implement your own views for accessing and authorizing tokens.
The `django-oauth-plus` package provides simple foundation for classic 'three-legged' oauth flow. Please refer to [the documentation][django-oauth-plus] for more details.
## OAuth2Authentication
This authentication uses [OAuth 2.0][rfc6749] authentication scheme. OAuth2 is more simple to work with than OAuth1, and provides much better security than simple token authentication. It is an unauthenticated scheme, and requires you to use an HTTPS connection.
This authentication class depends on the optional [django-oauth2-provider][django-oauth2-provider] project. In order to make it work you must install this package and add `provider` and `provider.oauth2` to your `INSTALLED_APPS`:
INSTALLED_APPS = (
...
'provider',
'provider.oauth2',
)
Then add `OAuth2Authentication` to your global `DEFAULT_AUTHENTICATION_CLASSES` setting:
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.OAuth2Authentication',
),
You must also include the following in your root `urls.py` module:
url(r'^oauth2/', include('provider.oauth2.urls', namespace='oauth2')),
Note that the `namespace='oauth2'` argument is required.
Finally, sync your database.
python manage.py syncdb
python manage.py migrate
---
**Note:** If you use `OAuth2Authentication` in production you must ensure that your API is only available over `https`.
---
#### Getting started with django-oauth2-provider
The `OAuth2Authentication` class only provides token verification for requests. It doesn't provide authorization flow for your clients.
The OAuth 2 authorization flow is taken care by the [django-oauth2-provider][django-oauth2-provider] dependency. A walkthrough is given here, but for more details you should refer to [the documentation][django-oauth2-provider-docs].
To get started:
##### 1. Create a client
You can create a client, either through the shell, or by using the Django admin.
Go to the admin panel and create a new `Provider.Client` entry. It will create the `client_id` and `client_secret` properties for you.
##### 2. Request an access token
To request an access token, submit a `POST` request to the url `/oauth2/access_token` with the following fields:
* `client_id` the client id you've just configured at the previous step.
* `client_secret` again configured at the previous step.
* `username` the username with which you want to log in.
* `password` well, that speaks for itself.
You can use the command line to test that your local configuration is working:
curl -X POST -d "client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&grant_type=password&username=YOUR_USERNAME&password=YOUR_PASSWORD" http://localhost:8000/oauth2/access_token/
You should get a response that looks something like this:
{"access_token": "<your-access-token>", "scope": "read", "expires_in": 86399, "refresh_token": "<your-refresh-token>"}
##### 3. Access the API
The only thing needed to make the `OAuth2Authentication` class work is to insert the `access_token` you've received in the `Authorization` request header.
The command line to test the authentication looks like:
curl -H "Authorization: Bearer <your-access-token>" http://localhost:8000/api/
### Alternative OAuth 2 implementations
Note that [Django OAuth Toolkit][django-oauth-toolkit] is an alternative external package that also includes OAuth 2.0 support for REST framework.
---
# Custom authentication
To implement a custom authentication scheme, subclass `BaseAuthentication` and override the `.authenticate(self, request)` method. The method should return a two-tuple of `(user, auth)` if authentication succeeds, or `None` otherwise.
@ -392,14 +290,49 @@ The following example will authenticate any incoming request as the user given b
The following third party packages are also available.
## Django OAuth Toolkit
The [Django OAuth Toolkit][django-oauth-toolkit] package provides OAuth 2.0 support, and works with Python 2.7 and Python 3.3+. The package is maintained by [Evonove][evonove] and uses the excellent [OAuthLib][oauthlib]. The package is well documented, and well supported and is currently our **recommended package for OAuth 2.0 support**.
#### Installation & configuration
Install using `pip`.
pip install django-oauth-toolkit
Add the package to your `INSTALLED_APPS` and modify your REST framework settings.
INSTALLED_APPS = (
...
'oauth2_provider',
)
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'oauth2_provider.ext.rest_framework.OAuth2Authentication',
)
}
For more details see the [Django REST framework - Getting started][django-oauth-toolkit-getting-started] documentation.
## Django REST framework OAuth
The [Django REST framework OAuth][django-rest-framework-oauth] package provides both OAuth1 and OAuth2 support for REST framework.
This package was previously included directly in REST framework but is now supported and maintained as a third party package.
#### Installation & configuration
Install the package using `pip`.
pip install djangorestframework-oauth
For details on configuration and usage see the Django REST framework OAuth documentation for [authentication][django-rest-framework-oauth-authentication] and [permissions][django-rest-framework-oauth-permissions].
## Digest Authentication
HTTP digest authentication is a widely implemented scheme that was intended to replace HTTP basic authentication, and which provides a simple encrypted authentication mechanism. [Juan Riaza][juanriaza] maintains the [djangorestframework-digestauth][djangorestframework-digestauth] package which provides HTTP digest authentication support for REST framework.
## Django OAuth Toolkit
The [Django OAuth Toolkit][django-oauth-toolkit] package provides OAuth 2.0 support, and works with Python 2.7 and Python 3.3+. The package is maintained by [Evonove][evonove] and uses the excellent [OAuthLib][oauthlib]. The package is well documented, and comes as a recommended alternative for OAuth 2.0 support.
## Django OAuth2 Consumer
The [Django OAuth2 Consumer][doac] library from [Rediker Software][rediker] is another package that provides [OAuth 2.0 support for REST framework][doac-rest-framework]. The package includes token scoping permissions on tokens, which allows finer-grained access to your API.
@ -431,6 +364,10 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a
[mod_wsgi_official]: http://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIPassAuthorization
[custom-user-model]: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#specifying-a-custom-user-model
[south-dependencies]: http://south.readthedocs.org/en/latest/dependencies.html
[django-oauth-toolkit-getting-started]: https://django-oauth-toolkit.readthedocs.org/en/latest/rest-framework/getting_started.html
[django-rest-framework-oauth]: http://jpadilla.github.io/django-rest-framework-oauth/
[django-rest-framework-oauth-authentication]: http://jpadilla.github.io/django-rest-framework-oauth/authentication/
[django-rest-framework-oauth-permissions]: http://jpadilla.github.io/django-rest-framework-oauth/permissions/
[juanriaza]: https://github.com/juanriaza
[djangorestframework-digestauth]: https://github.com/juanriaza/django-rest-framework-digestauth
[oauth-1.0a]: http://oauth.net/core/1.0a

View File

@ -61,10 +61,10 @@ In order to alter the style of the response, you could write the following custo
from rest_framework.views import exception_handler
def custom_exception_handler(exc):
def custom_exception_handler(exc, context):
# Call REST framework's default exception handler first,
# to get the standard error response.
response = exception_handler(exc)
response = exception_handler(exc, context)
# Now add the HTTP status code to the response.
if response is not None:
@ -138,6 +138,14 @@ Raised when an authenticated request fails the permission checks.
By default this exception results in a response with the HTTP status code "403 Forbidden".
## NotFound
**Signature:** `NotFound(detail=None)`
Raised when a resource does not exists at the given URL. This exception is equivalent to the standard `Http404` Django exception.
By default this exception results in a response with the HTTP status code "404 Not Found".
## MethodNotAllowed
**Signature:** `MethodNotAllowed(method, detail=None)`
@ -146,6 +154,14 @@ Raised when an incoming request occurs that does not map to a handler method on
By default this exception results in a response with the HTTP status code "405 Method Not Allowed".
## NotAcceptable
**Signature:** `NotAcceptable(detail=None)`
Raised when an incoming request occurs with an `Accept` header that cannot be satisfied by any of the available renderers.
By default this exception results in a response with the HTTP status code "406 Not Acceptable".
## UnsupportedMediaType
**Signature:** `UnsupportedMediaType(media_type, detail=None)`

View File

@ -138,11 +138,12 @@ A text representation. Optionally validates the text to be shorter than `max_len
Corresponds to `django.db.models.fields.CharField` or `django.db.models.fields.TextField`.
**Signature:** `CharField(max_length=None, min_length=None, allow_blank=False)`
**Signature:** `CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)`
- `max_length` - Validates that the input contains no more than this number of characters.
- `min_length` - Validates that the input contains no fewer than this number of characters.
- `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`.
- `trim_whitespace` - If set to `True` then leading and trailing whitespace is trimmed. Defaults to `True`.
The `allow_null` option is also available for string fields, although its usage is discouraged in favor of `allow_blank`. It is valid to set both `allow_blank=True` and `allow_null=True`, but doing so means that there will be two differing types of empty value permissible for string representations, which can lead to data inconsistencies and subtle application bugs.

View File

@ -6,147 +6,126 @@ source: pagination.py
>
> &mdash; [Django documentation][cite]
REST framework includes a `PaginationSerializer` class that makes it easy to return paginated data in a way that can then be rendered to arbitrary media types.
REST framework includes support for customizable pagination styles. This allows you to modify how large result sets are split into individual pages of data.
## Paginating basic data
The pagination API can support either:
Let's start by taking a look at an example from the Django documentation.
* Pagination links that are provided as part of the content of the response.
* Pagination links that are included in response headers, such as `Content-Range` or `Link`.
from django.core.paginator import Paginator
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.
objects = ['john', 'paul', 'george', 'ringo']
paginator = Paginator(objects, 2)
page = paginator.page(1)
page.object_list
# ['john', 'paul']
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.
At this point we've got a page object. If we wanted to return this page object as a JSON response, we'd need to provide the client with context such as next and previous links, so that it would be able to page through the remaining results.
## Setting the pagination style
from rest_framework.pagination import PaginationSerializer
serializer = PaginationSerializer(instance=page)
serializer.data
# {'count': 4, 'next': '?page=2', 'previous': None, 'results': [u'john', u'paul']}
The `context` argument of the `PaginationSerializer` class may optionally include the request. If the request is included in the context then the next and previous links returned by the serializer will use absolute URLs instead of relative URLs.
request = RequestFactory().get('/foobar')
serializer = PaginationSerializer(instance=page, context={'request': request})
serializer.data
# {'count': 4, 'next': 'http://testserver/foobar?page=2', 'previous': None, 'results': [u'john', u'paul']}
We could now return that data in a `Response` object, and it would be rendered into the correct media type.
## Paginating QuerySets
Our first example worked because we were using primitive objects. If we wanted to paginate a queryset or other complex data, we'd need to specify a serializer to use to serialize the result set itself.
We can do this using the `object_serializer_class` attribute on the inner `Meta` class of the pagination serializer. For example.
class UserSerializer(serializers.ModelSerializer):
"""
Serializes user querysets.
"""
class Meta:
model = User
fields = ('username', 'email')
class PaginatedUserSerializer(pagination.PaginationSerializer):
"""
Serializes page objects of user querysets.
"""
class Meta:
object_serializer_class = UserSerializer
We could now use our pagination serializer in a view like this.
@api_view('GET')
def user_list(request):
queryset = User.objects.all()
paginator = Paginator(queryset, 20)
page = request.QUERY_PARAMS.get('page')
try:
users = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
users = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999),
# deliver last page of results.
users = paginator.page(paginator.num_pages)
serializer_context = {'request': request}
serializer = PaginatedUserSerializer(users,
context=serializer_context)
return Response(serializer.data)
## Pagination in the generic views
The generic class based views `ListAPIView` and `ListCreateAPIView` provide pagination of the returned querysets by default. You can customise this behaviour by altering the pagination style, by modifying the default number of results, by allowing clients to override the page size using a query parameter, or by turning pagination off completely.
The default pagination style may be set globally, using the `DEFAULT_PAGINATION_SERIALIZER_CLASS`, `PAGINATE_BY`, `PAGINATE_BY_PARAM`, and `MAX_PAGINATE_BY` settings. For example.
The default pagination style may be set globally, using the `DEFAULT_PAGINATION_CLASS` settings key. For example, to use the built-in limit/offset pagination, you would do:
REST_FRAMEWORK = {
'PAGINATE_BY': 10, # Default to 10
'PAGINATE_BY_PARAM': 'page_size', # Allow client to override, using `?page_size=xxx`.
'MAX_PAGINATE_BY': 100 # Maximum limit allowed when using `?page_size=xxx`.
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination'
}
You can also set the pagination style on a per-view basis, using the `ListAPIView` generic class-based view.
You can also set the pagination class on an individual view by using the `pagination_class` attribute. Typically you'll want to use the same pagination style throughout your API, although you might want to vary individual aspects of the pagination, such as default or maximum page size, on a per-view basis.
class PaginatedListView(ListAPIView):
queryset = ExampleModel.objects.all()
serializer_class = ExampleModelSerializer
paginate_by = 10
## Modifying the pagination style
If you want to modify particular aspects of the pagination style, you'll want to override one of the pagination classes, and set the attributes that you want to change.
class LargeResultsSetPagination(PageNumberPagination):
paginate_by = 1000
paginate_by_param = 'page_size'
max_paginate_by = 100
max_paginate_by = 10000
Note that using a `paginate_by` value of `None` will turn off pagination for the view.
Note if you use the `PAGINATE_BY_PARAM` settings, you also have to set the `paginate_by_param` attribute in your view to `None` in order to turn off pagination for those requests that contain the `paginate_by_param` parameter.
class StandardResultsSetPagination(PageNumberPagination):
paginate_by = 100
paginate_by_param = 'page_size'
max_paginate_by = 1000
For more complex requirements such as serialization that differs depending on the requested media type you can override the `.get_paginate_by()` and `.get_pagination_serializer_class()` methods.
You can then apply your new style to a view using the `.pagination_class` attribute:
class BillingRecordsView(generics.ListAPIView):
queryset = Billing.objects.all()
serializer = BillingRecordsSerializer
pagination_class = LargeResultsSetPagination
Or apply the style globally, using the `DEFAULT_PAGINATION_CLASS` settings key. For example:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'apps.core.pagination.StandardResultsSetPagination' }
---
# Custom pagination serializers
# API Reference
To create a custom pagination serializer class you should override `pagination.BasePaginationSerializer` and set the fields that you want the serializer to return.
## PageNumberPagination
You can also override the name used for the object list field, by setting the `results_field` attribute, which defaults to `'results'`.
**TODO**
## LimitOffsetPagination
**TODO**
## CursorPagination
**TODO**
---
# Custom pagination styles
To create a custom pagination serializer class you should subclass `pagination.BasePagination` and override the `paginate_queryset(self, queryset, request, view=None)` and `get_paginated_response(self, data)` methods:
* The `paginate_queryset` method is passed the initial queryset and should return an iterable object that contains only the data in the requested page.
* The `get_paginated_response` method is passed the serialized page data and should return a `Response` instance.
Note that the `paginate_queryset` method may set state on the pagination instance, that may later be used by the `get_paginated_response` method.
## Example
For example, to nest a pair of links labelled 'prev' and 'next', and set the name for the results field to 'objects', you might use something like this.
Let's modify the built-in `PageNumberPagination` style, so that instead of include the pagination links in the body of the response, we'll instead include a `Link` header, in a [similar style to the GitHub API][github-link-pagination].
from rest_framework import pagination
from rest_framework import serializers
class LinkHeaderPagination(pagination.PageNumberPagination):
def get_paginated_response(self, data):
next_url = self.get_next_link() previous_url = self.get_previous_link()
class LinksSerializer(serializers.Serializer):
next = pagination.NextPageField(source='*')
prev = pagination.PreviousPageField(source='*')
if next_url is not None and previous_url is not None:
link = '<{next_url}; rel="next">, <{previous_url}; rel="prev">'
elif next_url is not None:
link = '<{next_url}; rel="next">'
elif previous_url is not None:
link = '<{previous_url}; rel="prev">'
else:
link = ''
class CustomPaginationSerializer(pagination.BasePaginationSerializer):
links = LinksSerializer(source='*') # Takes the page object as the source
total_results = serializers.ReadOnlyField(source='paginator.count')
link = link.format(next_url=next_url, previous_url=previous_url)
headers = {'Link': link} if link else {}
results_field = 'objects'
return Response(data, headers=headers)
## Using your custom pagination serializer
## Using your custom pagination class
To have your custom pagination serializer be used by default, use the `DEFAULT_PAGINATION_SERIALIZER_CLASS` setting:
To have your custom pagination class be used by default, use the `DEFAULT_PAGINATION_CLASS` setting:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_SERIALIZER_CLASS':
'example_app.pagination.CustomPaginationSerializer',
'DEFAULT_PAGINATION_CLASS': 'my_project.apps.core.pagination.LinkHeaderPagination',
'PAGINATE_BY': 10
}
Alternatively, to set your custom pagination serializer on a per-view basis, use the `pagination_serializer_class` attribute on a generic class based view:
API responses for list endpoints will now include a `Link` header, instead of including the pagination links as part of the body of the response, for example:
class PaginatedListView(generics.ListAPIView):
model = ExampleModel
pagination_serializer_class = CustomPaginationSerializer
paginate_by = 10
---
![Link Header][link-header]
*A custom pagination style, using the 'Link' header'*
---
# HTML pagination controls
## Customizing the controls
---
# Third party packages
@ -157,5 +136,7 @@ The following third party packages are also available.
The [`DRF-extensions` package][drf-extensions] includes a [`PaginateByMaxMixin` mixin class][paginate-by-max-mixin] that allows your API clients to specify `?page_size=max` to obtain the maximum allowed page size.
[cite]: https://docs.djangoproject.com/en/dev/topics/pagination/
[github-link-pagination]: https://developer.github.com/guides/traversing-with-pagination/
[link-header]: ../img/link-header-pagination.png
[drf-extensions]: http://chibisov.github.io/drf-extensions/docs/
[paginate-by-max-mixin]: http://chibisov.github.io/drf-extensions/docs/#paginatebymaxmixin

View File

@ -26,26 +26,26 @@ As an example, if you are sending `json` encoded data using jQuery with the [.aj
## Setting the parsers
The default set of parsers may be set globally, using the `DEFAULT_PARSER_CLASSES` setting. For example, the following settings would allow requests with `YAML` content.
The default set of parsers may be set globally, using the `DEFAULT_PARSER_CLASSES` setting. For example, the following settings would allow only requests with `JSON` content, instead of the default of JSON or form data.
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.YAMLParser',
'rest_framework.parsers.JSONParser',
)
}
You can also set the parsers used for an individual view, or viewset,
using the `APIView` class based views.
from rest_framework.parsers import YAMLParser
from rest_framework.response import Response
from rest_framework.parsers import JSONParser
from rest_framework.response import Response
from rest_framework.views import APIView
class ExampleView(APIView):
"""
A view that can accept POST requests with YAML content.
A view that can accept POST requests with JSON content.
"""
parser_classes = (YAMLParser,)
parser_classes = (JSONParser,)
def post(self, request, format=None):
return Response({'received data': request.data})
@ -53,10 +53,10 @@ using the `APIView` class based views.
Or, if you're using the `@api_view` decorator with function based views.
@api_view(['POST'])
@parser_classes((YAMLParser,))
@parser_classes((JSONParser,))
def example_view(request, format=None):
"""
A view that can accept POST requests with YAML content.
A view that can accept POST requests with JSON content.
"""
return Response({'received data': request.data})
@ -70,26 +70,6 @@ Parses `JSON` request content.
**.media_type**: `application/json`
## YAMLParser
Parses `YAML` request content.
Requires the `pyyaml` package to be installed.
**.media_type**: `application/yaml`
## XMLParser
Parses REST framework's default style of `XML` request content.
Note that the `XML` markup language is typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, and `XHTML`.
If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type.
Requires the `defusedxml` package to be installed.
**.media_type**: `application/xml`
## FormParser
Parses HTML form content. `request.data` will be populated with a `QueryDict` of data.
@ -182,6 +162,48 @@ The following is an example plaintext parser that will populate the `request.dat
The following third party packages are also available.
## YAML
[REST framework YAML][rest-framework-yaml] provides [YAML][yaml] parsing and rendering support. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.
#### Installation & configuration
Install using pip.
$ pip install djangorestframework-yaml
Modify your REST framework settings.
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': (
'rest_framework_yaml.parsers.YAMLParser',
),
'DEFAULT_RENDERER_CLASSES': (
'rest_framework_yaml.renderers.YAMLRenderer',
),
}
## XML
[REST Framework XML][rest-framework-xml] provides a simple informal XML format. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.
#### Installation & configuration
Install using pip.
$ pip install djangorestframework-xml
Modify your REST framework settings.
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': (
'rest_framework_xml.parsers.XMLParser',
),
'DEFAULT_RENDERER_CLASSES': (
'rest_framework_xml.renderers.XMLRenderer',
),
}
## MessagePack
[MessagePack][messagepack] is a fast, efficient binary serialization format. [Juan Riaza][juanriaza] maintains the [djangorestframework-msgpack][djangorestframework-msgpack] package which provides MessagePack renderer and parser support for REST framework.
@ -193,6 +215,9 @@ The following third party packages are also available.
[jquery-ajax]: http://api.jquery.com/jQuery.ajax/
[cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion
[upload-handlers]: https://docs.djangoproject.com/en/dev/topics/http/file-uploads/#upload-handlers
[rest-framework-yaml]: http://jpadilla.github.io/django-rest-framework-yaml/
[rest-framework-xml]: http://jpadilla.github.io/django-rest-framework-xml/
[yaml]: http://www.yaml.org/
[messagepack]: https://github.com/juanriaza/django-rest-framework-msgpack
[juanriaza]: https://github.com/juanriaza
[vbabiy]: https://github.com/vbabiy

View File

@ -166,21 +166,6 @@ As with `DjangoModelPermissions` you can use custom model permissions by overrid
---
## TokenHasReadWriteScope
This permission class is intended for use with either of the `OAuthAuthentication` and `OAuth2Authentication` classes, and ties into the scoping that their backends provide.
Requests with a safe methods of `GET`, `OPTIONS` or `HEAD` will be allowed if the authenticated token has read permission.
Requests for `POST`, `PUT`, `PATCH` and `DELETE` will be allowed if the authenticated token has write permission.
This permission class relies on the implementations of the [django-oauth-plus][django-oauth-plus] and [django-oauth2-provider][django-oauth2-provider] libraries, which both provide limited support for controlling the scope of access tokens:
* `django-oauth-plus`: Tokens are associated with a `Resource` class which has a `name`, `url` and `is_readonly` properties.
* `django-oauth2-provider`: Tokens are associated with a bitwise `scope` attribute, that defaults to providing bitwise values for `read` and/or `write`.
If you require more advanced scoping for your API, such as restricting tokens to accessing a subset of functionality of your API then you will need to provide a custom permission class. See the source of the `django-oauth-plus` or `django-oauth2-provider` package for more details on scoping token access.
---
# Custom permissions
@ -268,8 +253,6 @@ The [REST Condition][rest-condition] package is another extension for building c
[objectpermissions]: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#handling-object-permissions
[guardian]: https://github.com/lukaszb/django-guardian
[get_objects_for_user]: http://pythonhosted.org/django-guardian/api/guardian.shortcuts.html#get-objects-for-user
[django-oauth-plus]: http://code.larlet.fr/django-oauth-plus
[django-oauth2-provider]: https://github.com/caffeinehit/django-oauth2-provider
[2.2-announcement]: ../topics/2.2-announcement.md
[filtering]: filtering.md
[drf-any-permissions]: https://github.com/kevin-brown/drf-any-permissions

View File

@ -18,11 +18,11 @@ For more information see the documentation on [content negotiation][conneg].
## Setting the renderers
The default set of renderers may be set globally, using the `DEFAULT_RENDERER_CLASSES` setting. For example, the following settings would use `YAML` as the main media type and also include the self describing API.
The default set of renderers may be set globally, using the `DEFAULT_RENDERER_CLASSES` setting. For example, the following settings would use `JSON` as the main media type and also include the self describing API.
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.YAMLRenderer',
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
)
}
@ -31,15 +31,15 @@ You can also set the renderers used for an individual view, or viewset,
using the `APIView` class based views.
from django.contrib.auth.models import User
from rest_framework.renderers import JSONRenderer, YAMLRenderer
from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response
from rest_framework.views import APIView
class UserCountView(APIView):
"""
A view that returns the count of active users, in JSON or YAML.
A view that returns the count of active users in JSON.
"""
renderer_classes = (JSONRenderer, YAMLRenderer)
renderer_classes = (JSONRenderer, )
def get(self, request, format=None):
user_count = User.objects.filter(active=True).count()
@ -49,10 +49,10 @@ using the `APIView` class based views.
Or, if you're using the `@api_view` decorator with function based views.
@api_view(['GET'])
@renderer_classes((JSONRenderer, JSONPRenderer))
@renderer_classes((JSONRenderer,))
def user_count_view(request, format=None):
"""
A view that returns the count of active users, in JSON or JSONp.
A view that returns the count of active users in JSON.
"""
user_count = User.objects.filter(active=True).count()
content = {'user_count': user_count}
@ -93,72 +93,6 @@ The default JSON encoding style can be altered using the `UNICODE_JSON` and `COM
**.charset**: `None`
## JSONPRenderer
Renders the request data into `JSONP`. The `JSONP` media type provides a mechanism of allowing cross-domain AJAX requests, by wrapping a `JSON` response in a javascript callback.
The javascript callback function must be set by the client including a `callback` URL query parameter. For example `http://example.com/api/users?callback=jsonpCallback`. If the callback function is not explicitly set by the client it will default to `'callback'`.
---
**Warning**: If you require cross-domain AJAX requests, you should almost certainly be using the more modern approach of [CORS][cors] as an alternative to `JSONP`. See the [CORS documentation][cors-docs] for more details.
The `jsonp` approach is essentially a browser hack, and is [only appropriate for globally readable API endpoints][jsonp-security], where `GET` requests are unauthenticated and do not require any user permissions.
---
**.media_type**: `application/javascript`
**.format**: `'.jsonp'`
**.charset**: `utf-8`
## YAMLRenderer
Renders the request data into `YAML`.
Requires the `pyyaml` package to be installed.
Note that non-ascii characters will be rendered using `\uXXXX` character escape. For example:
unicode black star: "\u2605"
**.media_type**: `application/yaml`
**.format**: `'.yaml'`
**.charset**: `utf-8`
## UnicodeYAMLRenderer
Renders the request data into `YAML`.
Requires the `pyyaml` package to be installed.
Note that non-ascii characters will not be character escaped. For example:
unicode black star: ★
**.media_type**: `application/yaml`
**.format**: `'.yaml'`
**.charset**: `utf-8`
## XMLRenderer
Renders REST framework's default style of `XML` response content.
Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, and `XHTML`.
If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type.
**.media_type**: `application/xml`
**.format**: `'.xml'`
**.charset**: `utf-8`
## TemplateHTMLRenderer
Renders data to HTML, using Django's standard template rendering.
@ -408,13 +342,81 @@ Templates will render with a `RequestContext` which includes the `status_code` a
The following third party packages are also available.
## YAML
[REST framework YAML][rest-framework-yaml] provides [YAML][yaml] parsing and rendering support. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.
#### Installation & configuration
Install using pip.
$ pip install djangorestframework-yaml
Modify your REST framework settings.
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': (
'rest_framework_yaml.parsers.YAMLParser',
),
'DEFAULT_RENDERER_CLASSES': (
'rest_framework_yaml.renderers.YAMLRenderer',
),
}
## XML
[REST Framework XML][rest-framework-xml] provides a simple informal XML format. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.
#### Installation & configuration
Install using pip.
$ pip install djangorestframework-xml
Modify your REST framework settings.
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': (
'rest_framework_xml.parsers.XMLParser',
),
'DEFAULT_RENDERER_CLASSES': (
'rest_framework_xml.renderers.XMLRenderer',
),
}
## JSONP
[REST framework JSONP][rest-framework-jsonp] provides JSONP rendering support. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.
---
**Warning**: If you require cross-domain AJAX requests, you should generally be using the more modern approach of [CORS][cors] as an alternative to `JSONP`. See the [CORS documentation][cors-docs] for more details.
The `jsonp` approach is essentially a browser hack, and is [only appropriate for globally readable API endpoints][jsonp-security], where `GET` requests are unauthenticated and do not require any user permissions.
---
#### Installation & configuration
Install using pip.
$ pip install djangorestframework-jsonp
Modify your REST framework settings.
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework_yaml.renderers.JSONPRenderer',
),
}
## MessagePack
[MessagePack][messagepack] is a fast, efficient binary serialization format. [Juan Riaza][juanriaza] maintains the [djangorestframework-msgpack][djangorestframework-msgpack] package which provides MessagePack renderer and parser support for REST framework.
## CSV
Comma-separated values are a plain-text tabular data format, that can be easily imported into spreadsheet applications. [Mjumbe Poe][mjumbewu] maintains the [djangorestframework-csv][djangorestframework-csv] package which provides CSV renderer support for REST framework.
Comma-separated values are a plain-text tabular data format, that can be easily imported into spreadsheet applications. [Mjumbe Poe][mjumbewu] maintains the [djangorestframework-csv][djangorestframework-csv] package which provides CSV renderer support for REST framework.
## UltraJSON
@ -424,7 +426,6 @@ Comma-separated values are a plain-text tabular data format, that can be easily
[djangorestframework-camel-case] provides camel case JSON renderers and parsers for REST framework. This allows serializers to use Python-style underscored field names, but be exposed in the API as Javascript-style camel case field names. It is maintained by [Vitaly Babiy][vbabiy].
## Pandas (CSV, Excel, PNG)
[Django REST Pandas] provides a serializer and renderers that support additional data processing and output via the [Pandas] DataFrame API. Django REST Pandas includes renderers for Pandas-style CSV files, Excel workbooks (both `.xls` and `.xlsx`), and a number of [other formats]. It is maintained by [S. Andrew Sheppard][sheppard] as part of the [wq Project][wq].
@ -433,20 +434,25 @@ 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
[conneg]: content-negotiation.md
[browser-accept-headers]: http://www.gethifi.com/blog/browser-rest-http-accept-headers
[rfc4627]: http://www.ietf.org/rfc/rfc4627.txt
[cors]: http://www.w3.org/TR/cors/
[cors-docs]: ../topics/ajax-csrf-cors.md
[jsonp-security]: http://stackoverflow.com/questions/613962/is-jsonp-safe-to-use
[testing]: testing.md
[HATEOAS]: http://timelessrepo.com/haters-gonna-hateoas
[quote]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
[application/vnd.github+json]: http://developer.github.com/v3/media/
[application/vnd.collection+json]: http://www.amundsen.com/media-types/collection/
[django-error-views]: https://docs.djangoproject.com/en/dev/topics/http/views/#customizing-error-views
[rest-framework-jsonp]: http://jpadilla.github.io/django-rest-framework-jsonp/
[cors]: http://www.w3.org/TR/cors/
[cors-docs]: http://www.django-rest-framework.org/topics/ajax-csrf-cors/
[jsonp-security]: http://stackoverflow.com/questions/613962/is-jsonp-safe-to-use
[rest-framework-yaml]: http://jpadilla.github.io/django-rest-framework-yaml/
[rest-framework-xml]: http://jpadilla.github.io/django-rest-framework-xml/
[messagepack]: http://msgpack.org/
[juanriaza]: https://github.com/juanriaza
[mjumbewu]: https://github.com/mjumbewu
[vbabiy]: https://github.com/vbabiy
[rest-framework-yaml]: http://jpadilla.github.io/django-rest-framework-yaml/
[rest-framework-xml]: http://jpadilla.github.io/django-rest-framework-xml/
[yaml]: http://www.yaml.org/
[djangorestframework-msgpack]: https://github.com/juanriaza/django-rest-framework-msgpack
[djangorestframework-csv]: https://github.com/mjumbewu/django-rest-framework-csv
[ultrajson]: https://github.com/esnme/ultrajson

View File

@ -457,7 +457,7 @@ To do so, open the Django shell, using `python manage.py shell`, then import the
name = CharField(allow_blank=True, max_length=100, required=False)
owner = PrimaryKeyRelatedField(queryset=User.objects.all())
## Specifying which fields should be included
## 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`.
@ -499,7 +499,7 @@ You can add extra fields to a `ModelSerializer` or override the default fields b
Extra fields can correspond to any property or callable on the model.
## Specifying which fields should be read-only
## Specifying read only fields
You may wish to specify multiple fields as read-only. Instead of adding each field explicitly with the `read_only=True` attribute, you may use the shortcut Meta option, `read_only_fields`.
@ -528,7 +528,7 @@ Please review the [Validators Documentation](/api-guide/validators/) for details
---
## Specifying additional keyword arguments for fields.
## 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.
@ -567,6 +567,79 @@ The inner `Meta` class on serializers is not inherited from parent classes by de
Typically we would recommend *not* using inheritance on inner Meta classes, but instead declaring all options explicitly.
## Customizing field mappings
The ModelSerializer class also exposes an API that you can override in order to alter how serializer fields are automatically determined when instantiating the serializer.
Normally if a `ModelSerializer` does not generate the fields you need by default the you should either add them to the class explicitly, or simply use a regular `Serializer` class instead. However in some cases you may want to create a new base class that defines how the serializer fields are created for any given model.
### `.serializer_field_mapping`
A mapping of Django model classes to REST framework serializer classes. You can override this mapping to alter the default serializer classes that should be used for each model class.
### `.serializer_related_field`
This property should be the serializer field class, that is used for relational fields by default.
For `ModelSerializer` this defaults to `PrimaryKeyRelatedField`.
For `HyperlinkedModelSerializer` this defaults to `serializers.HyperlinkedRelatedField`.
### `serializer_url_field`
The serializer field class that should be used for any `url` field on the serializer.
Defaults to `serializers.HyperlinkedIdentityField`
### `serializer_choice_field`
The serializer field class that should be used for any choice fields on the serializer.
Defaults to `serializers.ChoiceField`
### The field_class and field_kwargs API
The following methods are called to determine the class and keyword arguments for each field that should be automatically included on the serializer. Each of these methods should return a two tuple of `(field_class, field_kwargs)`.
### `.build_standard_field(self, field_name, model_field)`
Called to generate a serializer field that maps to a standard model field.
The default implementation returns a serializer class based on the `serializer_field_mapping` attribute.
### `.build_relational_field(self, field_name, relation_info)`
Called to generate a serializer field that maps to a relational model field.
The default implementation returns a serializer class based on the `serializer_relational_field` attribute.
The `relation_info` argument is a named tuple, that contains `model_field`, `related_model`, `to_many` and `has_through_model` properties.
### `.build_nested_field(self, field_name, relation_info, nested_depth)`
Called to generate a serializer field that maps to a relational model field, when the `depth` option has been set.
The default implementation dynamically creates a nested serializer class based on either `ModelSerializer` or `HyperlinkedModelSerializer`.
The `nested_depth` will be the value of the `depth` option, minus one.
The `relation_info` argument is a named tuple, that contains `model_field`, `related_model`, `to_many` and `has_through_model` properties.
### `.build_property_field(self, field_name, model_class)`
Called to generate a serializer field that maps to a property or zero-argument method on the model class.
The default implementation returns a `ReadOnlyField` class.
### `.build_url_field(self, field_name, model_class)`
Called to generate a serializer field for the serializer's own `url` field. The default implementation returns a `HyperlinkedIdentityField` class.
### `.build_unknown_field(self, field_name, model_class)`
Called when the field name did not map to any model field or model property.
The default implementation raises an error, although subclasses may customize this behavior.
---
# HyperlinkedModelSerializer

View File

@ -12,10 +12,10 @@ For example your project's `settings.py` file might include something like this:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.YAMLRenderer',
'rest_framework.renderers.JSONRenderer',
),
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.YAMLParser',
'rest_framework.parsers.JSONParser',
)
}
@ -166,6 +166,28 @@ Default: `ordering`
---
## Versioning settings
#### DEFAULT_VERSION
The value that should be used for `request.version` when no versioning information is present.
Default: `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.
Default: `None`
#### VERSION_PARAMETER
The string that should used for any versioning parameters, such as in the media type or URL query parameters.
Default: `'version'`
---
## Authentication settings
*The following settings control the behavior of unauthenticated requests.*
@ -393,7 +415,7 @@ This setting can be changed to support error responses other than the default `{
This should be a function with the following signature:
exception_handler(exc)
exception_handler(exc, context)
* `exc`: The exception.

View File

@ -255,14 +255,14 @@ The default format used to make test requests may be set using the `TEST_REQUEST
If you need to test requests using something other than multipart or json requests, you can do so by setting the `TEST_REQUEST_RENDERER_CLASSES` setting.
For example, to add support for using `format='yaml'` in test requests, you might have something like this in your `settings.py` file.
For example, to add support for using `format='html'` in test requests, you might have something like this in your `settings.py` file.
REST_FRAMEWORK = {
...
'TEST_REQUEST_RENDERER_CLASSES': (
'rest_framework.renderers.MultiPartRenderer',
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.YAMLRenderer'
'rest_framework.renderers.TemplateHTMLRenderer'
)
}

View File

@ -0,0 +1,205 @@
source: versioning.py
# Versioning
> Versioning an interface is just a "polite" way to kill deployed clients.
>
> &mdash; [Roy Fielding][cite].
API versioning allows you to alter behavior between different clients. REST framework provides for a number of different versioning schemes.
Versioning is determined by the incoming client request, and may either be based on the request URL, or based on the request headers.
There are a number of valid approaches to approaching versioning. [Non-versioned systems can also be appropriate][roy-fielding-on-versioning], particularly if you're engineering for very long-term systems with multiple clients outside of your control.
## Versioning with REST framework
When API versioning is enabled, the `request.version` attribute will contain a string that corresponds to the version requested in the incoming client request.
By default, versioning is not enabled, and `request.version` will always return `None`.
#### Varying behavior based on the version
How you vary the API behavior is up to you, but one example you might typically want is to switch to a different serialization style in a newer version. For example:
def get_serializer_class(self):
if self.request.version == 'v1':
return AccountSerializerVersion1
return AccountSerializer
#### Reversing URLs for versioned APIs
The `reverse` function included by REST framework ties in with the versioning scheme. You need to make sure to include the current `request` as a keyword argument, like so.
reverse('bookings-list', request=request)
The above function will apply any URL transformations appropriate to the request version. For example:
* If `NamespacedVersioning` was being used, and the API version was 'v1', then the URL lookup used would be `'v1:bookings-list'`, which might resolve to a URL like `http://example.org/v1/bookings/`.
* If `QueryParameterVersioning` was being used, and the API version was `1.0`, then the returned URL might be something like `http://example.org/bookings/?version=1.0`
#### Versioned APIs and hyperlinked serializers
When using hyperlinked serialization styles together with a URL based versioning scheme make sure to include the request as context to the serializer.
def get(self, request):
queryset = Booking.objects.all()
serializer = BookingsSerializer(queryset, many=True, context={'request': request})
return Response({'all_bookings': serializer.data})
Doing so will allow any returned URLs to include the appropriate versioning.
## Configuring the versioning scheme
The versioning scheme is defined by the `DEFAULT_VERSIONING_CLASS` settings key.
REST_FRAMEWORK = {
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning'
}
Unless it is explicitly set, the value for `DEFAULT_VERSIONING_CLASS` will be `None`. In this case the `request.version` attribute will always return `None`.
You can also set the versioning scheme on an individual view. Typically you won't need to do this, as it makes more sense to have a single versioning scheme used globally. If you do need to do so, use the `versioning_class` attribute.
class ProfileList(APIView):
versioning_class = versioning.QueryParameterVersioning
#### Other versioning settings
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`.
* `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. 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'`.
---
# API Reference
## AcceptHeaderVersioning
This scheme requires the client to specify the version as part of the media type in the `Accept` header. The version is included as a media type parameter, that supplements the main media type.
Here's an example HTTP request using the accept header versioning style.
GET /bookings/ HTTP/1.1
Host: example.com
Accept: application/json; version=1.0
In the example request above `request.version` attribute would return the string `'1.0'`.
Versioning based on accept headers is [generally considered][klabnik-guidelines] as [best practice][heroku-guidelines], although other styles may be suitable depending on your client requirements.
#### Using accept headers with vendor media types
Strictly speaking the `json` media type is not specified as [including additional parameters][json-parameters]. If you are building a well-specified public API you might consider using a [vendor media type][vendor-media-type]. To do so, configure your renderers to use a JSON based renderer with a custom media type:
class BookingsAPIRenderer(JSONRenderer):
media_type = 'application/vnd.megacorp.bookings+json'
Your client requests would now look like this:
GET /bookings/ HTTP/1.1
Host: example.com
Accept: application/vnd.megacorp.bookings+json; version=1.0
## URLParameterVersioning
This scheme requires the client to specify the version as part of the URL path.
GET /v1/bookings/ HTTP/1.1
Host: example.com
Accept: application/json
Your URL conf must include a pattern that matches the version with a `'version'` keyword argument, so that this information is available to the versioning scheme.
urlpatterns = [
url(
r'^(?P<version>{v1,v2})/bookings/$',
bookings_list,
name='bookings-list'
),
url(
r'^(?P<version>{v1,v2})/bookings/(?P<pk>[0-9]+)/$',
bookings_detail,
name='bookings-detail'
)
]
## NamespaceVersioning
To the client, this scheme is the same as `URLParameterVersioning`. The only difference is how it is configured in your Django application, as it uses URL namespacing, instead of URL keyword arguments.
GET /v1/something/ HTTP/1.1
Host: example.com
Accept: application/json
With this scheme the `request.version` attribute is determined based on the `namespace` that matches the incoming request path.
In the following example we're giving a set of views two different possible URL prefixes, each under a different namespace:
# bookings/urls.py
urlpatterns = [
url(r'^$', bookings_list, name='bookings-list'),
url(r'^(?P<pk>[0-9]+)/$', bookings_detail, name='bookings-detail')
]
# urls.py
urlpatterns = [
url(r'^v1/bookings/', include('bookings.urls', namespace='v1')),
url(r'^v2/bookings/', include('bookings.urls', namespace='v2'))
]
Both `URLParameterVersioning` and `NamespaceVersioning` are reasonable if you just need a simple versioning scheme. The `URLParameterVersioning` approach might be better suitable for small ad-hoc projects, and the `NamespaceVersioning` is probably easier to manage for larger projects.
## HostNameVersioning
The hostname versioning scheme requires the client to specify the requested version as part of the hostname in the URL.
For example the following is an HTTP request to the `http://v1.example.com/bookings/` URL:
GET /bookings/ HTTP/1.1
Host: v1.example.com
Accept: application/json
By default this implementation expects the hostname to match this simple regular expression:
^([a-zA-Z0-9]+)\.[a-zA-Z0-9]+\.[a-zA-Z0-9]+$
Note that the first group is enclosed in brackets, indicating that this is the matched portion of the hostname.
The `HostNameVersioning` scheme can be awkward to use in debug mode as you will typically be accessing a raw IP address such as `127.0.0.1`. There are various online services which you to [access localhost with a custom subdomain][lvh] which you may find helpful in this case.
Hostname based versioning can be particularly useful if you have requirements to route incoming requests to different servers based on the version, as you can configure different DNS records for different API versions.
## QueryParameterVersioning
This scheme is a simple style that includes the version as a query parameter in the URL. For example:
GET /something/?version=0.1 HTTP/1.1
Host: example.com
Accept: application/json
---
# Custom versioning schemes
To implement a custom versioning scheme, subclass `BaseVersioning` and override the `.determine_version` method.
## Example
The following example uses a custom `X-API-Version` header to determine the requested version.
class XAPIVersionScheme(versioning.BaseVersioning):
def determine_version(self, request, *args, **kwargs):
return request.META.get('HTTP_X_API_VERSION', None)
If your versioning scheme is based on the request URL, you will also want to alter how versioned URLs are determined. In order to do so you should override the `.reverse()` method on the class. See the source code for examples.
[cite]: http://www.slideshare.net/evolve_conference/201308-fielding-evolve/31
[roy-fielding-on-versioning]: http://www.infoq.com/articles/roy-fielding-on-versioning
[klabnik-guidelines]: http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http#i_want_my_api_to_be_versioned
[heroku-guidelines]: https://github.com/interagent/http-api-design#version-with-accepts-header
[json-parameters]: http://tools.ietf.org/html/rfc4627#section-6
[vendor-media-type]: http://en.wikipedia.org/wiki/Internet_media_type#Vendor_tree
[lvh]: https://reinteractive.net/posts/199-developing-and-testing-rails-applications-with-subdomains

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -28,13 +28,12 @@ For more details see the [3.0 release notes][3.0-announcement].
<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>
Django REST framework is a powerful and flexible toolkit that makes it easy to build Web APIs.
Some reasons you might want to use REST framework:
* The [Web browsable API][sandbox] is a huge usability win for your developers.
* [Authentication policies][authentication] including [OAuth1a][oauth1-section] and [OAuth2][oauth2-section] out of the box.
* [Authentication policies][authentication] including packages for [OAuth1a][oauth1-section] and [OAuth2][oauth2-section].
* [Serialization][serializers] that supports both [ORM][modelserializer-section] and [non-ORM][serializer-section] data sources.
* Customizable all the way down - just use [regular function-based views][functionview-section] if you don't need the [more][generic-views] [powerful][viewsets] [features][routers].
* [Extensive documentation][index], and [great community support][group].
@ -56,15 +55,9 @@ REST framework requires the following:
The following packages are optional:
* [Markdown][markdown] (2.1.0+) - Markdown support for the browsable API.
* [PyYAML][yaml] (3.10+) - YAML content-type support.
* [defusedxml][defusedxml] (0.3+) - XML content-type support.
* [django-filter][django-filter] (0.9.2+) - Filtering support.
* [django-oauth-plus][django-oauth-plus] (2.0+) and [oauth2][oauth2] (1.5.211+) - OAuth 1.0a support.
* [django-oauth2-provider][django-oauth2-provider] (0.2.3+) - OAuth 2.0 support.
* [django-guardian][django-guardian] (1.1.1+) - Object level permissions support.
**Note**: The `oauth2` Python package is badly misnamed, and actually provides OAuth 1.0a support. Also note that packages required for both OAuth 1.0a, and OAuth 2.0 are not yet Python 3 compatible.
## Installation
Install using `pip`, including any optional packages you want...
@ -180,6 +173,7 @@ The API guide is your complete reference manual to all the functionality provide
* [Throttling][throttling]
* [Filtering][filtering]
* [Pagination][pagination]
* [Versioning][versioning]
* [Content negotiation][contentnegotiation]
* [Metadata][metadata]
* [Format suffixes][formatsuffixes]
@ -201,14 +195,10 @@ General guides to using REST framework.
* [Third Party Resources][third-party-resources]
* [Contributing to REST framework][contributing]
* [Project management][project-management]
* [2.0 Announcement][rest-framework-2-announcement]
* [2.2 Announcement][2.2-announcement]
* [2.3 Announcement][2.3-announcement]
* [2.4 Announcement][2.4-announcement]
* [3.0 Announcement][3.0-announcement]
* [3.1 Announcement][3.1-announcement]
* [Kickstarter Announcement][kickstarter-announcement]
* [Release Notes][release-notes]
* [Credits][credits]
## Development
@ -263,18 +253,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[mozilla]: http://www.mozilla.org/en-US/about/
[eventbrite]: https://www.eventbrite.co.uk/about/
[markdown]: http://pypi.python.org/pypi/Markdown/
[yaml]: http://pypi.python.org/pypi/PyYAML
[defusedxml]: https://pypi.python.org/pypi/defusedxml
[django-filter]: http://pypi.python.org/pypi/django-filter
[oauth2]: https://github.com/simplegeo/python-oauth2
[django-oauth-plus]: https://bitbucket.org/david/django-oauth-plus/wiki/Home
[django-oauth2-provider]: https://github.com/caffeinehit/django-oauth2-provider
[django-guardian]: https://github.com/lukaszb/django-guardian
[0.4]: https://github.com/tomchristie/django-rest-framework/tree/0.4.X
[image]: img/quickstart.png
[index]: .
[oauth1-section]: api-guide/authentication#oauthauthentication
[oauth2-section]: api-guide/authentication#oauth2authentication
[oauth1-section]: api-guide/authentication/#django-rest-framework-oauth
[oauth2-section]: api-guide/authentication/#django-oauth-toolkit
[serializer-section]: api-guide/serializers#serializers
[modelserializer-section]: api-guide/serializers#modelserializer
[functionview-section]: api-guide/views#function-based-views
@ -305,6 +290,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[throttling]: api-guide/throttling.md
[filtering]: api-guide/filtering.md
[pagination]: api-guide/pagination.md
[versioning]: api-guide/versioning.md
[contentnegotiation]: api-guide/content-negotiation.md
[metadata]: api-guide/metadata.md
[formatsuffixes]: api-guide/format-suffixes.md
@ -315,6 +301,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[settings]: api-guide/settings.md
[documenting-your-api]: topics/documenting-your-api.md
[internationalization]: topics/documenting-your-api.md
[ajax-csrf-cors]: topics/ajax-csrf-cors.md
[browser-enhancements]: topics/browser-enhancements.md
[browsableapi]: topics/browsable-api.md
@ -322,14 +309,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[contributing]: topics/contributing.md
[project-management]: topics/project-management.md
[third-party-resources]: topics/third-party-resources.md
[rest-framework-2-announcement]: topics/rest-framework-2-announcement.md
[2.2-announcement]: topics/2.2-announcement.md
[2.3-announcement]: topics/2.3-announcement.md
[2.4-announcement]: topics/2.4-announcement.md
[3.0-announcement]: topics/3.0-announcement.md
[3.1-announcement]: topics/3.1-announcement.md
[kickstarter-announcement]: topics/kickstarter-announcement.md
[release-notes]: topics/release-notes.md
[credits]: topics/credits.md
[tox]: http://testrun.org/tox/latest/

View File

@ -0,0 +1,196 @@
# Django REST framework 3.1
The 3.1 release is an intermediate step in the Kickstarter project releases, and includes a range of new functionality.
Some highlights include:
* A super-smart cursor pagination scheme.
* An improved pagination API, supporting header or in-body pagination styles.
* Pagination controls rendering in the browsable API.
* Better support for API versioning.
* Built-in internationalization support.
* Support for Django 1.8's `HStoreField` and `ArrayField`.
---
## Pagination
The pagination API has been improved, making it both easier to use, and more powerful.
#### New pagination schemes.
Until now, there has only been a single built-in pagination style in REST framework. We now have page, limit/offset and cursor based schemes included by default.
The cursor based pagination scheme is particularly smart, and is a better approach for clients iterating through large or frequently changing result sets. The scheme supports paging against non-unique indexes, by using both cursor and limit/offset information. It also allows for both forward and reverse cursor pagination. Much credit goes to David Cramer for [this blog post](http://cramer.io/2011/03/08/building-cursors-for-the-disqus-api/) on the subject.
#### Pagination controls in the browsable API.
Paginated results now include controls that render directly in the browsable API. If you're using the page or limit/offset style, then you'll see a page based control displayed in the browsable API:
![page number based pagination](../img/pages-pagination.png )
The cursor based pagination renders a more simple style of control:
![cursor based pagination](../img/cursor-pagination.png )
#### Support for header-based pagination.
The pagination API was previously only able to alter the pagination style in the body of the response. The API now supports being able to write pagination information in response headers, making it possible to use pagination schemes that use the `Link` or `Content-Range` headers.
For more information, see the [custom pagination styles](../api-guide/pagination/#custom-pagination-styles) documentation.
---
## Versioning
We've made it easier to build versioned APIs. Built-in schemes for versioning include both URL based and Accept header based variations.
When using a URL based scheme, hyperlinked serializers will resolve relationships to the same API version as used on the incoming request.
For example, when using `NamespaceVersioning`, and the following hyperlinked serializer:
class AccountsSerializer(serializer.HyperlinkedModelSerializer):
class Meta:
model = Accounts
fields = ('account_name', 'users')
The output representation would match the version used on the incoming request. Like so:
GET http://example.org/v2/accounts/10 # Version 'v2'
{
"account_name": "europa",
"users": [
"http://example.org/v2/users/12", # Version 'v2'
"http://example.org/v2/users/54",
"http://example.org/v2/users/87"
]
}
---
## Internationalization
REST framework now includes a built-in set of translations, and supports internationalized error responses. This allows you to either change the default language, or to allow clients to specify the language via the `Accept-Language` header.
You can change the default language by using the standard Django `LANGUAGE_CODE` setting:
LANGUAGE_CODE = "es-es"
You can turn on per-request language requests by adding `LocalMiddleware` to your `MIDDLEWARE_CLASSES` setting:
MIDDLEWARE_CLASSES = [
...
'django.middleware.locale.LocaleMiddleware'
]
When per-request internationalization is enabled, client requests will respect the `Accept-Language` header where possible. For example, let's make a request for an unsupported media type:
**Request**
GET /api/users HTTP/1.1
Accept: application/xml
Accept-Language: es-es
Host: example.org
**Response**
HTTP/1.0 406 NOT ACCEPTABLE
{
"detail": "No se ha podido satisfacer la solicitud de cabecera de Accept."
}
Note that the structure of the error responses is still the same. We still have a `details` key in the response. If needed you can modify this behavior too, by using a [custom exception handler][custom-exception-handler].
We include built-in translations both for standard exception cases, and for serializer validation errors.
The full list of supported languages can be found on our [Transifex project page](https://www.transifex.com/projects/p/django-rest-framework/).
If you only wish to support a subset of the supported languages, use Django's standard `LANGUAGES` setting:
LANGUAGES = [
('de', _('German')),
('en', _('English')),
]
For more details, see the [internationalization documentation](internationalization.md).
Many thanks to [Craig Blaszczyk](https://github.com/jakul) for helping push this through.
---
## New field types
Django 1.8's new `ArrayField`, `HStoreField` and `UUIDField` are now all fully supported.
This work also means that we now have both `serializers.DictField()`, and `serializers.ListField()` types, allowing you to express and validate a wider set of representations.
If you're building a new 1.8 project, then you should probably consider using `UUIDField` as the primary keys for all your models. This style will work automatically with hyperlinked serializers, returning URLs in the following style:
http://example.org/api/purchases/9b1a433f-e90d-4948-848b-300fdc26365d
---
## ModelSerializer API
The serializer redesign in 3.0 did not include any public API for modifying how ModelSerializer classes automatically generate a set of fields from a given mode class. We've now re-introduced an API for this, allowing you to create new ModelSerializer base classes that behave differently, such as using a different default style for relationships.
For more information, see the documentation on [customizing field mappings](../api-guide/serializers/#customizing-field-mappings) for ModelSerializer classes.
---
## Moving packages out of core
We've now moved a number of packages out of the core of REST framework, and into separately installable packages. If you're currently using these you don't need to worry, you simply need to `pip install` the new packages, and change any import paths.
We're making this change in order to help distribute the maintainance workload, and keep better focus of the core essentials of the framework.
The change also means we can be more flexible with which external packages we recommend. For example, the excellently maintained [Django OAuth toolkit](https://github.com/evonove/django-oauth-toolkit) has now been promoted as our recommended option for integrating OAuth support.
The following packages are now moved out of core and should be separately installed:
* OAuth - [djangorestframework-oauth](http://jpadilla.github.io/django-rest-framework-oauth/)
* XML - [djangorestframework-xml](http://jpadilla.github.io/django-rest-framework-xml)
* YAML - [djangorestframework-yaml](http://jpadilla.github.io/django-rest-framework-yaml)
* JSONP - [djangorestframework-jsonp](http://jpadilla.github.io/django-rest-framework-jsonp)
It's worth reiterating that this change in policy shouldn't mean any work in your codebase other than adding a new requirement and modifying some import paths. For example to install XML rendering, you would now do:
pip install djangorestframework-xml
And modify your settings, like so:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
'rest_framework_xml.renderers.XMLRenderer'
]
}
Thanks go to the latest member of our maintenance team, [José Padilla](https://github.com/jpadilla/), for handling this work and taking on ownership of these packages.
---
## Deprecations
The `request.DATA`, `request.FILES` and `request.QUERY_PARAMS` attributes move from pending deprecation, to deprecated. Use `request.data` and `request.query_params` instead, as discussed in the 3.0 release notes.
The ModelSerializer Meta options for `write_only_fields`, `view_name` and `lookup_field` are also moved from pending deprecation, to deprecated. Use `extra_kwargs` instead, as discussed in the 3.0 release notes.
All these attributes and options will still work in 3.1, but their usage will raise a warning. They will be fully removed in 3.2.
---
## What's next?
The next focus will be on HTML renderings of API output and will include:
* HTML form rendering of serializers.
* Filtering controls built-in to the browsable API.
* An alternative admin-style interface.
This will either be made as a single 3.2 release, or split across two separate releases, with the HTML forms and filter controls coming in 3.2, and the admin-style interface coming in a 3.3 release.
[custom-exception-handler]: ../api-guide/exceptions.md#custom-exception-handling

View File

@ -1,404 +0,0 @@
# Credits
The following people have helped make REST framework great.
* Tom Christie - [tomchristie]
* Marko Tibold - [markotibold]
* Paul Miller - [paulmillr]
* Sébastien Piquemal - [sebpiq]
* Carmen Wick - [cwick]
* Alex Ehlke - [aehlke]
* Alen Mujezinovic - [flashingpumpkin]
* Carles Barrobés - [txels]
* Michael Fötsch - [mfoetsch]
* David Larlet - [david]
* Andrew Straw - [astraw]
* Zeth - [zeth]
* Fernando Zunino - [fzunino]
* Jens Alm - [ulmus]
* Craig Blaszczyk - [jakul]
* Garcia Solero - [garciasolero]
* Tom Drummond - [devioustree]
* Danilo Bargen - [dbrgn]
* Andrew McCloud - [amccloud]
* Thomas Steinacher - [thomasst]
* Meurig Freeman - [meurig]
* Anthony Nemitz - [anemitz]
* Ewoud Kohl van Wijngaarden - [ekohl]
* Michael Ding - [yandy]
* Mjumbe Poe - [mjumbewu]
* Natim - [natim]
* Sebastian Żurek - [sebzur]
* Benoit C - [dzen]
* Chris Pickett - [bunchesofdonald]
* Ben Timby - [btimby]
* Michele Lazzeri - [michelelazzeri-nextage]
* Camille Harang - [mammique]
* Paul Oswald - [poswald]
* Sean C. Farley - [scfarley]
* Daniel Izquierdo - [izquierdo]
* Can Yavuz - [tschan]
* Shawn Lewis - [shawnlewis]
* Alec Perkins - [alecperkins]
* Michael Barrett - [phobologic]
* Mathieu Dhondt - [laundromat]
* Johan Charpentier - [cyberj]
* Jamie Matthews - [j4mie]
* Mattbo - [mattbo]
* Max Hurl - [maximilianhurl]
* Tomi Pajunen - [eofs]
* Rob Dobson - [rdobson]
* Daniel Vaca Araujo - [diviei]
* Madis Väin - [madisvain]
* Stephan Groß - [minddust]
* Pavel Savchenko - [asfaltboy]
* Otto Yiu - [ottoyiu]
* Jacob Magnusson - [jmagnusson]
* Osiloke Harold Emoekpere - [osiloke]
* Michael Shepanski - [mjs7231]
* Toni Michel - [tonimichel]
* Ben Konrath - [benkonrath]
* Marc Aymerich - [glic3rinu]
* Ludwig Kraatz - [ludwigkraatz]
* Rob Romano - [robromano]
* Eugene Mechanism - [mechanism]
* Jonas Liljestrand - [jonlil]
* Justin Davis - [irrelative]
* Dustin Bachrach - [dbachrach]
* Mark Shirley - [maspwr]
* Olivier Aubert - [oaubert]
* Yuri Prezument - [yprez]
* Fabian Buechler - [fabianbuechler]
* Mark Hughes - [mhsparks]
* Michael van de Waeter - [mvdwaeter]
* Reinout van Rees - [reinout]
* Michael Richards - [justanotherbody]
* Ben Roberts - [roberts81]
* Venkata Subramanian Mahalingam - [annacoder]
* George Kappel - [gkappel]
* Colin Murtaugh - [cmurtaugh]
* Simon Pantzare - [pilt]
* Szymon Teżewski - [sunscrapers]
* Joel Marcotte - [joual]
* Trey Hunner - [treyhunner]
* Roman Akinfold - [akinfold]
* Toran Billups - [toranb]
* Sébastien Béal - [sebastibe]
* Andrew Hankinson - [ahankinson]
* Juan Riaza - [juanriaza]
* Michael Mior - [michaelmior]
* Marc Tamlyn - [mjtamlyn]
* Richard Wackerbarth - [wackerbarth]
* Johannes Spielmann - [shezi]
* James Cleveland - [radiosilence]
* Steve Gregory - [steve-gregory]
* Federico Capoano - [nemesisdesign]
* Bruno Renié - [brutasse]
* Kevin Stone - [kevinastone]
* Guglielmo Celata - [guglielmo]
* Mike Tums - [mktums]
* Michael Elovskikh - [wronglink]
* Michał Jaworski - [swistakm]
* Andrea de Marco - [z4r]
* Fernando Rocha - [fernandogrd]
* Xavier Ordoquy - [xordoquy]
* Adam Wentz - [floppya]
* Andreas Pelme - [pelme]
* Ryan Detzel - [ryanrdetzel]
* Omer Katz - [thedrow]
* Wiliam Souza - [waa]
* Jonas Braun - [iekadou]
* Ian Dash - [bitmonkey]
* Bouke Haarsma - [bouke]
* Pierre Dulac - [dulaccc]
* Dave Kuhn - [kuhnza]
* Sitong Peng - [stoneg]
* Victor Shih - [vshih]
* Atle Frenvik Sveen - [atlefren]
* J Paul Reed - [preed]
* Matt Majewski - [forgingdestiny]
* Jerome Chen - [chenjyw]
* Andrew Hughes - [eyepulp]
* Daniel Hepper - [dhepper]
* Hamish Campbell - [hamishcampbell]
* Marlon Bailey - [avinash240]
* James Summerfield - [jsummerfield]
* Andy Freeland - [rouge8]
* Craig de Stigter - [craigds]
* Pablo Recio - [pyriku]
* Brian Zambrano - [brianz]
* Òscar Vilaplana - [grimborg]
* Ryan Kaskel - [ryankask]
* Andy McKay - [andymckay]
* Matteo Suppo - [matteosuppo]
* Karol Majta - [lolek09]
* David Jones - [commonorgarden]
* Andrew Tarzwell - [atarzwell]
* Michal Dvořák - [mikee2185]
* Markus Törnqvist - [mjtorn]
* Pascal Borreli - [pborreli]
* Alex Burgel - [aburgel]
* David Medina - [copitux]
* Areski Belaid - [areski]
* Ethan Freman - [mindlace]
* David Sanders - [davesque]
* Philip Douglas - [freakydug]
* Igor Kalat - [trwired]
* Rudolf Olah - [omouse]
* Gertjan Oude Lohuis - [gertjanol]
* Matthias Jacob - [cyroxx]
* Pavel Zinovkin - [pzinovkin]
* Will Kahn-Greene - [willkg]
* Kevin Brown - [kevin-brown]
* Rodrigo Martell - [coderigo]
* James Rutherford - [jimr]
* Ricky Rosario - [rlr]
* Veronica Lynn - [kolvia]
* Dan Stephenson - [etos]
* Martin Clement - [martync]
* Jeremy Satterfield - [jsatt]
* Christopher Paolini - [chrispaolini]
* Filipe A Ximenes - [filipeximenes]
* Ramiro Morales - [ramiro]
* Krzysztof Jurewicz - [krzysiekj]
* Eric Buehl - [ericbuehl]
* Kristian Øllegaard - [kristianoellegaard]
* Alexander Akhmetov - [alexander-akhmetov]
* Andrey Antukh - [niwibe]
* Mathieu Pillard - [diox]
* Edmond Wong - [edmondwong]
* Ben Reilly - [bwreilly]
* Tai Lee - [mrmachine]
* Markus Kaiserswerth - [mkai]
* Henry Clifford - [hcliff]
* Thomas Badaud - [badale]
* Colin Huang - [tamakisquare]
* Ross McFarland - [ross]
* Jacek Bzdak - [jbzdak]
* Alexander Lukanin - [alexanderlukanin13]
* Yamila Moreno - [yamila-moreno]
* Rob Hudson - [robhudson]
* Alex Good - [alexjg]
* Ian Foote - [ian-foote]
* Chuck Harmston - [chuckharmston]
* Philip Forget - [philipforget]
* Artem Mezhenin - [amezhenin]
Many thanks to everyone who's contributed to the project.
## Additional thanks
The documentation is built with [Bootstrap] and [Markdown].
Project hosting is with [GitHub].
Continuous integration testing is managed with [Travis CI][travis-ci].
The [live sandbox][sandbox] is hosted on [Heroku].
Various inspiration taken from the [Rails], [Piston], [Tastypie], [Dagny] and [django-viewsets] projects.
Development of REST framework 2.0 was sponsored by [DabApps].
## Contact
For usage questions please see the [REST framework discussion group][group].
You can also contact [@_tomchristie][twitter] directly on twitter.
[twitter]: http://twitter.com/_tomchristie
[bootstrap]: http://twitter.github.com/bootstrap/
[markdown]: http://daringfireball.net/projects/markdown/
[github]: https://github.com/tomchristie/django-rest-framework
[travis-ci]: https://secure.travis-ci.org/tomchristie/django-rest-framework
[rails]: http://rubyonrails.org/
[piston]: https://bitbucket.org/jespern/django-piston
[tastypie]: https://github.com/toastdriven/django-tastypie
[dagny]: https://github.com/zacharyvoase/dagny
[django-viewsets]: https://github.com/BertrandBordage/django-viewsets
[dabapps]: http://lab.dabapps.com
[sandbox]: http://restframework.herokuapp.com/
[heroku]: http://www.heroku.com/
[group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework
[tomchristie]: https://github.com/tomchristie
[markotibold]: https://github.com/markotibold
[paulmillr]: https://github.com/paulmillr
[sebpiq]: https://github.com/sebpiq
[cwick]: https://github.com/cwick
[aehlke]: https://github.com/aehlke
[flashingpumpkin]: https://github.com/flashingpumpkin
[txels]: https://github.com/txels
[mfoetsch]: https://github.com/mfoetsch
[david]: https://github.com/david
[astraw]: https://github.com/astraw
[zeth]: https://github.com/zeth
[fzunino]: https://github.com/fzunino
[ulmus]: https://github.com/ulmus
[jakul]: https://github.com/jakul
[garciasolero]: https://github.com/garciasolero
[devioustree]: https://github.com/devioustree
[dbrgn]: https://github.com/dbrgn
[amccloud]: https://github.com/amccloud
[thomasst]: https://github.com/thomasst
[meurig]: https://github.com/meurig
[anemitz]: https://github.com/anemitz
[ekohl]: https://github.com/ekohl
[yandy]: https://github.com/yandy
[mjumbewu]: https://github.com/mjumbewu
[natim]: https://github.com/natim
[sebzur]: https://github.com/sebzur
[dzen]: https://github.com/dzen
[bunchesofdonald]: https://github.com/bunchesofdonald
[btimby]: https://github.com/btimby
[michelelazzeri-nextage]: https://github.com/michelelazzeri-nextage
[mammique]: https://github.com/mammique
[poswald]: https://github.com/poswald
[scfarley]: https://github.com/scfarley
[izquierdo]: https://github.com/izquierdo
[tschan]: https://github.com/tschan
[shawnlewis]: https://github.com/shawnlewis
[alecperkins]: https://github.com/alecperkins
[phobologic]: https://github.com/phobologic
[laundromat]: https://github.com/laundromat
[cyberj]: https://github.com/cyberj
[j4mie]: https://github.com/j4mie
[mattbo]: https://github.com/mattbo
[maximilianhurl]: https://github.com/maximilianhurl
[eofs]: https://github.com/eofs
[rdobson]: https://github.com/rdobson
[diviei]: https://github.com/diviei
[madisvain]: https://github.com/madisvain
[minddust]: https://github.com/minddust
[asfaltboy]: https://github.com/asfaltboy
[ottoyiu]: https://github.com/OttoYiu
[jmagnusson]: https://github.com/jmagnusson
[osiloke]: https://github.com/osiloke
[mjs7231]: https://github.com/mjs7231
[tonimichel]: https://github.com/tonimichel
[benkonrath]: https://github.com/benkonrath
[glic3rinu]: https://github.com/glic3rinu
[ludwigkraatz]: https://github.com/ludwigkraatz
[robromano]: https://github.com/robromano
[mechanism]: https://github.com/mechanism
[jonlil]: https://github.com/jonlil
[irrelative]: https://github.com/irrelative
[dbachrach]: https://github.com/dbachrach
[maspwr]: https://github.com/maspwr
[oaubert]: https://github.com/oaubert
[yprez]: https://github.com/yprez
[fabianbuechler]: https://github.com/fabianbuechler
[mhsparks]: https://github.com/mhsparks
[mvdwaeter]: https://github.com/mvdwaeter
[reinout]: https://github.com/reinout
[justanotherbody]: https://github.com/justanotherbody
[roberts81]: https://github.com/roberts81
[annacoder]: https://github.com/annacoder
[gkappel]: https://github.com/gkappel
[cmurtaugh]: https://github.com/cmurtaugh
[pilt]: https://github.com/pilt
[sunscrapers]: https://github.com/sunscrapers
[joual]: https://github.com/joual
[treyhunner]: https://github.com/treyhunner
[akinfold]: https://github.com/akinfold
[toranb]: https://github.com/toranb
[sebastibe]: https://github.com/sebastibe
[ahankinson]: https://github.com/ahankinson
[juanriaza]: https://github.com/juanriaza
[michaelmior]: https://github.com/michaelmior
[mjtamlyn]: https://github.com/mjtamlyn
[wackerbarth]: https://github.com/wackerbarth
[shezi]: https://github.com/shezi
[radiosilence]: https://github.com/radiosilence
[steve-gregory]: https://github.com/steve-gregory
[nemesisdesign]: https://github.com/nemesisdesign
[brutasse]: https://github.com/brutasse
[kevinastone]: https://github.com/kevinastone
[guglielmo]: https://github.com/guglielmo
[mktums]: https://github.com/mktums
[wronglink]: https://github.com/wronglink
[swistakm]: https://github.com/swistakm
[z4r]: https://github.com/z4r
[fernandogrd]: https://github.com/fernandogrd
[xordoquy]: https://github.com/xordoquy
[floppya]: https://github.com/floppya
[pelme]: https://github.com/pelme
[ryanrdetzel]: https://github.com/ryanrdetzel
[thedrow]: https://github.com/thedrow
[waa]: https://github.com/wiliamsouza
[iekadou]: https://github.com/iekadou
[bitmonkey]: https://github.com/bitmonkey
[bouke]: https://github.com/bouke
[dulaccc]: https://github.com/dulaccc
[kuhnza]: https://github.com/kuhnza
[stoneg]: https://github.com/stoneg
[vshih]: https://github.com/vshih
[atlefren]: https://github.com/atlefren
[preed]: https://github.com/preed
[forgingdestiny]: https://github.com/forgingdestiny
[chenjyw]: https://github.com/chenjyw
[eyepulp]: https://github.com/eyepulp
[dhepper]: https://github.com/dhepper
[hamishcampbell]: https://github.com/hamishcampbell
[avinash240]: https://github.com/avinash240
[jsummerfield]: https://github.com/jsummerfield
[rouge8]: https://github.com/rouge8
[craigds]: https://github.com/craigds
[pyriku]: https://github.com/pyriku
[brianz]: https://github.com/brianz
[grimborg]: https://github.com/grimborg
[ryankask]: https://github.com/ryankask
[andymckay]: https://github.com/andymckay
[matteosuppo]: https://github.com/matteosuppo
[lolek09]: https://github.com/lolek09
[commonorgarden]: https://github.com/commonorgarden
[atarzwell]: https://github.com/atarzwell
[mikee2185]: https://github.com/mikee2185
[mjtorn]: https://github.com/mjtorn
[pborreli]: https://github.com/pborreli
[aburgel]: https://github.com/aburgel
[copitux]: https://github.com/copitux
[areski]: https://github.com/areski
[mindlace]: https://github.com/mindlace
[davesque]: https://github.com/davesque
[freakydug]: https://github.com/freakydug
[trwired]: https://github.com/trwired
[omouse]: https://github.com/omouse
[gertjanol]: https://github.com/gertjanol
[cyroxx]: https://github.com/cyroxx
[pzinovkin]: https://github.com/pzinovkin
[coderigo]: https://github.com/coderigo
[willkg]: https://github.com/willkg
[kevin-brown]: https://github.com/kevin-brown
[jimr]: https://github.com/jimr
[rlr]: https://github.com/rlr
[kolvia]: https://github.com/kolvia
[etos]: https://github.com/etos
[martync]: https://github.com/martync
[jsatt]: https://github.com/jsatt
[chrispaolini]: https://github.com/chrispaolini
[filipeximenes]: https://github.com/filipeximenes
[ramiro]: https://github.com/ramiro
[krzysiekj]: https://github.com/krzysiekj
[ericbuehl]: https://github.com/ericbuehl
[kristianoellegaard]: https://github.com/kristianoellegaard
[alexander-akhmetov]: https://github.com/alexander-akhmetov
[niwibe]: https://github.com/niwibe
[diox]: https://github.com/diox
[edmondwong]: https://github.com/edmondwong
[bwreilly]: https://github.com/bwreilly
[mrmachine]: https://github.com/mrmachine
[mkai]: https://github.com/mkai
[hcliff]: https://github.com/hcliff
[badale]: https://github.com/badale
[tamakisquare]: https://github.com/tamakisquare
[ross]: https://github.com/ross
[jbzdak]: https://github.com/jbzdak
[alexanderlukanin13]: https://github.com/alexanderlukanin13
[yamila-moreno]: https://github.com/yamila-moreno
[robhudson]: https://github.com/robhudson
[alexjg]: https://github.com/alexjg
[ian-foote]: https://github.com/ian-foote
[chuckharmston]: https://github.com/chuckharmston
[philipforget]: https://github.com/philipforget
[amezhenin]: https://github.com/amezhenin

View File

@ -0,0 +1,113 @@
# Internationalization
> Supporting internationalization is not optional. It must be a core feature.
>
> &mdash; [Jannis Leidel, speaking at Django Under the Hood, 2015][cite].
REST framework ships with translatable error messages. You can make these appear in your language enabling [Django's standard translation mechanisms][django-translation].
Doing so will allow you to:
* Select a language other than English as the default, using the standard `LANGUAGE_CODE` Django setting.
* Allow clients to choose a language themselves, using the `LocaleMiddleware` included with Django. A typical usage for API clients would be to include an `Accept-Language` request header.
## Enabling internationalized APIs
You can change the default language by using the standard Django `LANGUAGE_CODE` setting:
LANGUAGE_CODE = "es-es"
You can turn on per-request language requests by adding `LocalMiddleware` to your `MIDDLEWARE_CLASSES` setting:
MIDDLEWARE_CLASSES = [
...
'django.middleware.locale.LocaleMiddleware'
]
When per-request internationalization is enabled, client requests will respect the `Accept-Language` header where possible. For example, let's make a request for an unsupported media type:
**Request**
GET /api/users HTTP/1.1
Accept: application/xml
Accept-Language: es-es
Host: example.org
**Response**
HTTP/1.0 406 NOT ACCEPTABLE
{"detail": "No se ha podido satisfacer la solicitud de cabecera de Accept."}
REST framework includes these built-in translations both for standard exception cases, and for serializer validation errors.
Note that the translations only apply to the error strings themselves. The format of error messages, and the keys of field names will remain the same. An example `400 Bad Request` response body might look like this:
{"detail": {"username": ["Esse campo deve ser unico."]}}
If you want to use different string for parts of the response such as `detail` and `non_field_errors` then you can modify this behavior by using a [custom exception handler][custom-exception-handler].
#### Specifying the set of supported languages.
By default all available languages will be supported.
If you only wish to support a subset of the available languages, use Django's standard `LANGUAGES` setting:
LANGUAGES = [
('de', _('German')),
('en', _('English')),
]
## Adding new translations
REST framework translations are managed online using [Transifex][transifex-project]. You can use the Transifex service to add new translation languages. The maintenance team will then ensure that these translation strings are included in the REST framework package.
Sometimes you may need to add translation strings to your project locally. You may need to do this if:
* You want to use REST Framework in a language which has not been translated yet on Transifex.
* Your project includes custom error messages, which are not part of REST framework's default translation strings.
#### Translating a new language locally
This guide assumes you are already familiar with how to translate a Django app. If you're not, start by reading [Django's translation docs][django-translation].
If you're translating a new language you'll need to translate the existing REST framework error messages:
1. Make a new folder where you want to store the internationalization resources. Add this path to your [`LOCALE_PATHS`][django-locale-paths] setting.
2. Now create a subfolder for the language you want to translate. The folder should be named using [locale name][django-locale-name] notation. For example: `de`, `pt_BR`, `es_AR`.
3. Now copy the [base translations file][django-po-source] from the REST framework source code into your translations folder.
4. Edit the `django.po` file you've just copied, translating all the error messages.
5. Run `manage.py compilemessages -l pt_BR` to make the translations
available for Django to use. You should see a message like `processing file django.po in <...>/locale/pt_BR/LC_MESSAGES`.
6. Restart your development server to see the changes take effect.
If you're only translating custom error messages that exist inside your project codebase you don't need to copy the REST framework source `django.po` file into a `LOCALE_PATHS` folder, and can instead simply run Django's standard `makemessages` process.
## How the language is determined
If you want to allow per-request language preferences you'll need to include `django.middleware.locale.LocaleMiddleware` in your `MIDDLEWARE_CLASSES` setting.
You can find more information on how the language preference is determined in the [Django documentation][django-language-preference]. For reference, the method is:
1. First, it looks for the language prefix in the requested URL.
2. Failing that, it looks for the `LANGUAGE_SESSION_KEY` key in the current users session.
3. Failing that, it looks for a cookie.
4. Failing that, it looks at the `Accept-Language` HTTP header.
5. Failing that, it uses the global `LANGUAGE_CODE` setting.
For API clients the most appropriate of these will typically be to use the `Accept-Language` header; Sessions and cookies will not be available unless using session authentication, and generally better practice to prefer an `Accept-Language` header for API clients rather than using language URL prefixes.
[cite]: http://youtu.be/Wa0VfS2q94Y
[django-translation]: https://docs.djangoproject.com/en/1.7/topics/i18n/translation
[custom-exception-handler]: ../api-guide/exceptions.md#custom-exception-handling
[transifex-project]: https://www.transifex.com/projects/p/django-rest-framework/
[django-po-source]: https://raw.githubusercontent.com/tomchristie/django-rest-framework/master/rest_framework/locale/en_US/LC_MESSAGES/django.po
[django-language-preference]: https://docs.djangoproject.com/en/1.7/topics/i18n/translation/#how-django-discovers-language-preference
[django-locale-paths]: https://docs.djangoproject.com/en/1.7/ref/settings/#std:setting-LOCALE_PATHS
[django-locale-name]: https://docs.djangoproject.com/en/1.7/topics/i18n/#term-locale-name
[contributing]: ../../CONTRIBUTING.md

View File

@ -65,10 +65,11 @@ The following template should be used for the description of the issue, and serv
Team members have the following responsibilities.
* Add triage labels and milestones to tickets.
* Close invalid or resolved tickets.
* Add triage labels and milestones to tickets.
* Merge finalized pull requests.
* Build and deploy the documentation, using `mkdocs gh-deploy`.
* Build and update the included translation packs.
Further notes for maintainers:
@ -116,6 +117,55 @@ When pushing the release to PyPI ensure that your environment has been installed
---
## Translations
The maintenance team are responsible for managing the translation packs include in REST framework. Translating the source strings into multiple languages is managed through the [transifex service][transifex-project].
### Managing Transifex
The [official Transifex client][transifex-client] is used to upload and download translations to Transifex. The client is installed using pip:
pip install transifex-client
To use it you'll need a login to Transifex which has a password, and you'll need to have administrative access to the Transifex project. You'll need to create a `~/.transifexrc` file which contains your credentials.
[https://www.transifex.com]
username = ***
token = ***
password = ***
hostname = https://www.transifex.com
### Upload new source files
When any user visible strings are changed, they should be uploaded to Transifex so that the translators can start to translate them. To do this, just run:
# 1. Update the source django.po file, which is the US English version.
cd rest_framework
django-admin.py makemessages -l en_US
# 2. Push the source django.po file to Transifex.
cd ..
tx push -s
When pushing source files, Transifex will update the source strings of a resource to match those from the new source file.
Here's how differences between the old and new source files will be handled:
* New strings will be added.
* Modified strings will be added as well.
* Strings which do not exist in the new source file will be removed from the database, along with their translations. If that source strings gets re-added later then [Transifex Translation Memory][translation-memory] will automatically include the translation string.
### Download translations
When a translator has finished translating their work needs to be downloaded from Transifex into the REST framework repository. To do this, run:
# 3. Pull the translated django.po files from Transifex.
tx pull -a
cd rest_framework
# 4. Compile the binary .mo files for all supported languages.
django-admin.py compilemessages
---
## Project ownership
The PyPI package is owned by `@tomchristie`. As a backup `@j4mie` also has ownership of the package.
@ -134,6 +184,9 @@ The following issues still need to be addressed:
[bus-factor]: http://en.wikipedia.org/wiki/Bus_factor
[un-triaged]: https://github.com/tomchristie/django-rest-framework/issues?q=is%3Aopen+no%3Alabel
[transifex-project]: https://www.transifex.com/projects/p/django-rest-framework/
[transifex-client]: https://pypi.python.org/pypi/transifex-client
[translation-memory]: http://docs.transifex.com/guides/tm#let-tm-automatically-populate-translations
[github-org]: https://github.com/tomchristie/django-rest-framework/issues/2162
[sandbox]: http://restframework.herokuapp.com/
[mailing-list]: https://groups.google.com/forum/#!forum/django-rest-framework

View File

@ -137,598 +137,19 @@ For full details see the [3.0 release announcement](3.0-announcement.md).
---
## 2.4.x series
### 2.4.4
**Date**: [3rd November 2014](https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%222.4.4+Release%22+).
* **Security fix**: Escape URLs when replacing `format=` query parameter, as used in dropdown on `GET` button in browsable API to allow explicit selection of JSON vs HTML output.
* Maintain ordering of URLs in API root view for `DefaultRouter`.
* Fix `follow=True` in `APIRequestFactory`
* Resolve issue with invalid `read_only=True`, `required=True` fields being automatically generated by `ModelSerializer` in some cases.
* Resolve issue with `OPTIONS` requests returning incorrect information for views using `get_serializer_class` to dynamically determine serializer based on request method.
### 2.4.3
**Date**: [19th September 2014](https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%222.4.3+Release%22+).
* Support translatable view docstrings being displayed in the browsable API.
* Support [encoded `filename*`][rfc-6266] in raw file uploads with `FileUploadParser`.
* Allow routers to support viewsets that don't include any list routes or that don't include any detail routes.
* Don't render an empty login control in browsable API if `login` view is not included.
* CSRF exemption performed in `.as_view()` to prevent accidental omission if overriding `.dispatch()`.
* Login on browsable API now displays validation errors.
* Bugfix: Fix migration in `authtoken` application.
* Bugfix: Allow selection of integer keys in nested choices.
* Bugfix: Return `None` instead of `'None'` in `CharField` with `allow_none=True`.
* Bugfix: Ensure custom model fields map to equivelent serializer fields more reliably.
* Bugfix: `DjangoFilterBackend` no longer quietly changes queryset ordering.
### 2.4.2
**Date**: [3rd September 2014](https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%222.4.2+Release%22+).
* Bugfix: Fix broken pagination for 2.4.x series.
### 2.4.1
**Date**: [1st September 2014](https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%222.4.1+Release%22+).
* Bugfix: Fix broken login template for browsable API.
### 2.4.0
**Date**: [29th August 2014](https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%222.4.0+Release%22+).
**Django version requirements**: The lowest supported version of Django is now 1.4.2.
**South version requirements**: This note applies to any users using the optional `authtoken` application, which includes an associated database migration. You must now *either* upgrade your `south` package to version 1.0, *or* instead use the built-in migration support available with Django 1.7.
* Added compatibility with Django 1.7's database migration support.
* New test runner, using `py.test`.
* Deprecated `.model` view attribute in favor of explicit `.queryset` and `.serializer_class` attributes. The `DEFAULT_MODEL_SERIALIZER_CLASS` setting is also deprecated.
* `@detail_route` and `@list_route` decorators replace `@action` and `@link`.
* Support customizable view name and description functions, using the `VIEW_NAME_FUNCTION` and `VIEW_DESCRIPTION_FUNCTION` settings.
* Added `NUM_PROXIES` setting for smarter client IP identification.
* Added `MAX_PAGINATE_BY` setting and `max_paginate_by` generic view attribute.
* Added `Retry-After` header to throttled responses, as per [RFC 6585](http://tools.ietf.org/html/rfc6585). This should now be used in preference to the custom `X-Trottle-Wait-Seconds` header which will be fully deprecated in 3.0.
* Added `cache` attribute to throttles to allow overriding of default cache.
* Added `lookup_value_regex` attribute to routers, to allow the URL argument matching to be constrainted by the user.
* Added `allow_none` option to `CharField`.
* Support Django's standard `status_code` class attribute on responses.
* More intuitive behavior on the test client, as `client.logout()` now also removes any credentials that have been set.
* Bugfix: `?page_size=0` query parameter now falls back to default page size for view, instead of always turning pagination off.
* Bugfix: Always uppercase `X-Http-Method-Override` methods.
* Bugfix: Copy `filter_backends` list before returning it, in order to prevent view code from mutating the class attribute itself.
* Bugfix: Set the `.action` attribute on viewsets when introspected by `OPTIONS` for testing permissions on the view.
* Bugfix: Ensure `ValueError` raised during deserialization results in a error list rather than a single error. This is now consistent with other validation errors.
* Bugfix: Fix `cache_format` typo on throttle classes, was `"throtte_%(scope)s_%(ident)s"`. Note that this will invalidate existing throttle caches.
---
## 2.3.x series
### 2.3.14
**Date**: 12th June 2014
* **Security fix**: Escape request path when it is include as part of the login and logout links in the browsable API.
* `help_text` and `verbose_name` automatically set for related fields on `ModelSerializer`.
* Fix nested serializers linked through a backward foreign key relation.
* Fix bad links for the `BrowsableAPIRenderer` with `YAMLRenderer`.
* Add `UnicodeYAMLRenderer` that extends `YAMLRenderer` with unicode.
* Fix `parse_header` argument convertion.
* Fix mediatype detection under Python 3.
* Web browsable API now offers blank option on dropdown when the field is not required.
* `APIException` representation improved for logging purposes.
* Allow source="*" within nested serializers.
* Better support for custom oauth2 provider backends.
* Fix field validation if it's optional and has no value.
* Add `SEARCH_PARAM` and `ORDERING_PARAM`.
* Fix `APIRequestFactory` to support arguments within the url string for GET.
* Allow three transport modes for access tokens when accessing a protected resource.
* Fix `QueryDict` encoding on request objects.
* Ensure throttle keys do not contain spaces, as those are invalid if using `memcached`.
* Support `blank_display_value` on `ChoiceField`.
### 2.3.13
**Date**: 6th March 2014
* Django 1.7 Support.
* Fix `default` argument when used with serializer relation fields.
* Display the media type of the content that is being displayed in the browsable API, rather than 'text/html'.
* Bugfix for `urlize` template failure when URL regex is matched, but value does not `urlparse`.
* Use `urandom` for token generation.
* Only use `Vary: Accept` when more than one renderer exists.
### 2.3.12
**Date**: 15th January 2014
* **Security fix**: `OrderingField` now only allows ordering on readable serializer fields, or on fields explicitly specified using `ordering_fields`. This prevents users being able to order by fields that are not visible in the API, and exploiting the ordering of sensitive data such as password hashes.
* Bugfix: `write_only = True` fields now display in the browsable API.
### 2.3.11
**Date**: 14th January 2014
* Added `write_only` serializer field argument.
* Added `write_only_fields` option to `ModelSerializer` classes.
* JSON renderer now deals with objects that implement a dict-like interface.
* Fix compatiblity with newer versions of `django-oauth-plus`.
* Bugfix: Refine behavior that calls model manager `all()` across nested serializer relationships, preventing erronous behavior with some non-ORM objects, and preventing unnecessary queryset re-evaluations.
* Bugfix: Allow defaults on BooleanFields to be properly honored when values are not supplied.
* Bugfix: Prevent double-escaping of non-latin1 URL query params when appending `format=json` params.
### 2.3.10
**Date**: 6th December 2013
* Add in choices information for ChoiceFields in response to `OPTIONS` requests.
* Added `pre_delete()` and `post_delete()` method hooks.
* Added status code category helper functions.
* Bugfix: Partial updates which erronously set a related field to `None` now correctly fail validation instead of raising an exception.
* Bugfix: Responses without any content no longer include an HTTP `'Content-Type'` header.
* Bugfix: Correctly handle validation errors in PUT-as-create case, responding with 400.
### 2.3.9
**Date**: 15th November 2013
* Fix Django 1.6 exception API compatibility issue caused by `ValidationError`.
* Include errors in HTML forms in browsable API.
* Added JSON renderer support for numpy scalars.
* Added `transform_<fieldname>` hooks on serializers for easily modifying field output.
* Added `get_context` hook in `BrowsableAPIRenderer`.
* Allow serializers to be passed `files` but no `data`.
* `HTMLFormRenderer` now renders serializers directly to HTML without needing to create an intermediate form object.
* Added `get_filter_backends` hook.
* Added queryset aggregates to allowed fields in `OrderingFilter`.
* Bugfix: Fix decimal suppoprt with `YAMLRenderer`.
* Bugfix: Fix submission of unicode in browsable API through raw data form.
### 2.3.8
**Date**: 11th September 2013
* Added `DjangoObjectPermissions`, and `DjangoObjectPermissionsFilter`.
* Support customizable exception handling, using the `EXCEPTION_HANDLER` setting.
* Support customizable view name and description functions, using the `VIEW_NAME_FUNCTION` and `VIEW_DESCRIPTION_FUNCTION` settings.
* Added `MAX_PAGINATE_BY` setting and `max_paginate_by` generic view attribute.
* Added `cache` attribute to throttles to allow overriding of default cache.
* 'Raw data' tab in browsable API now contains pre-populated data.
* 'Raw data' and 'HTML form' tab preference in browsable API now saved between page views.
* Bugfix: `required=True` argument fixed for boolean serializer fields.
* Bugfix: `client.force_authenticate(None)` should also clear session info if it exists.
* Bugfix: Client sending empty string instead of file now clears `FileField`.
* Bugfix: Empty values on ChoiceFields with `required=False` now consistently return `None`.
* Bugfix: Clients setting `page_size=0` now simply returns the default page size, instead of disabling pagination. [*]
---
[*] Note that the change in `page_size=0` behaviour fixes what is considered to be a bug in how clients can effect the pagination size. However if you were relying on this behavior you will need to add the following mixin to your list views in order to preserve the existing behavior.
class DisablePaginationMixin(object):
def get_paginate_by(self, queryset=None):
if self.request.QUERY_PARAMS[self.paginate_by_param] == '0':
return None
return super(DisablePaginationMixin, self).get_paginate_by(queryset)
---
### 2.3.7
**Date**: 16th August 2013
* Added `APITestClient`, `APIRequestFactory` and `APITestCase` etc...
* Refactor `SessionAuthentication` to allow esier override for CSRF exemption.
* Remove 'Hold down "Control" message from help_text' widget messaging when not appropriate.
* Added admin configuration for auth tokens.
* Bugfix: `AnonRateThrottle` fixed to not throttle authenticated users.
* Bugfix: Don't set `X-Throttle-Wait-Seconds` when throttle does not have `wait` value.
* Bugfix: Fixed `PATCH` button title in browsable API.
* Bugfix: Fix issue with OAuth2 provider naive datetimes.
### 2.3.6
**Date**: 27th June 2013
* Added `trailing_slash` option to routers.
* Include support for `HttpStreamingResponse`.
* Support wider range of default serializer validation when used with custom model fields.
* UTF-8 Support for browsable API descriptions.
* OAuth2 provider uses timezone aware datetimes when supported.
* Bugfix: Return error correctly when OAuth non-existent consumer occurs.
* Bugfix: Allow `FileUploadParser` to correctly filename if provided as URL kwarg.
* Bugfix: Fix `ScopedRateThrottle`.
### 2.3.5
**Date**: 3rd June 2013
* Added `get_url` hook to `HyperlinkedIdentityField`.
* Serializer field `default` argument may be a callable.
* `@action` decorator now accepts a `methods` argument.
* Bugfix: `request.user` should be still be accessible in renderer context if authentication fails.
* Bugfix: The `lookup_field` option on `HyperlinkedIdentityField` should apply by default to the url field on the serializer.
* Bugfix: `HyperlinkedIdentityField` should continue to support `pk_url_kwarg`, `slug_url_kwarg`, `slug_field`, in a pending deprecation state.
* Bugfix: Ensure we always return 404 instead of 500 if a lookup field cannot be converted to the correct lookup type. (Eg non-numeric `AutoInteger` pk lookup)
### 2.3.4
**Date**: 24th May 2013
* Serializer fields now support `label` and `help_text`.
* Added `UnicodeJSONRenderer`.
* `OPTIONS` requests now return metadata about fields for `POST` and `PUT` requests.
* Bugfix: `charset` now properly included in `Content-Type` of responses.
* Bugfix: Blank choice now added in browsable API on nullable relationships.
* Bugfix: Many to many relationships with `through` tables are now read-only.
* Bugfix: Serializer fields now respect model field args such as `max_length`.
* Bugfix: SlugField now performs slug validation.
* Bugfix: Lazy-translatable strings now properly serialized.
* Bugfix: Browsable API now supports bootswatch styles properly.
* Bugfix: HyperlinkedIdentityField now uses `lookup_field` kwarg.
**Note**: Responses now correctly include an appropriate charset on the `Content-Type` header. For example: `application/json; charset=utf-8`. If you have tests that check the content type of responses, you may need to update these accordingly.
### 2.3.3
**Date**: 16th May 2013
* Added SearchFilter
* Added OrderingFilter
* Added GenericViewSet
* Bugfix: Multiple `@action` and `@link` methods now allowed on viewsets.
* Bugfix: Fix API Root view issue with DjangoModelPermissions
### 2.3.2
**Date**: 8th May 2013
* Bugfix: Fix `TIME_FORMAT`, `DATETIME_FORMAT` and `DATE_FORMAT` settings.
* Bugfix: Fix `DjangoFilterBackend` issue, failing when used on view with queryset attribute.
### 2.3.1
**Date**: 7th May 2013
* Bugfix: Fix breadcrumb rendering issue.
### 2.3.0
**Date**: 7th May 2013
* ViewSets and Routers.
* ModelSerializers support reverse relations in 'fields' option.
* HyperLinkedModelSerializers support 'id' field in 'fields' option.
* Cleaner generic views.
* Support for multiple filter classes.
* FileUploadParser support for raw file uploads.
* DecimalField support.
* Made Login template easier to restyle.
* Bugfix: Fix issue with depth>1 on ModelSerializer.
**Note**: See the [2.3 announcement][2.3-announcement] for full details.
---
## 2.2.x series
### 2.2.7
**Date**: 17th April 2013
* Loud failure when view does not return a `Response` or `HttpResponse`.
* Bugfix: Fix for Django 1.3 compatibility.
* Bugfix: Allow overridden `get_object()` to work correctly.
### 2.2.6
**Date**: 4th April 2013
* OAuth2 authentication no longer requires unnecessary URL parameters in addition to the token.
* URL hyperlinking in browsable API now handles more cases correctly.
* Long HTTP headers in browsable API are broken in multiple lines when possible.
* Bugfix: Fix regression with DjangoFilterBackend not worthing correctly with single object views.
* Bugfix: OAuth should fail hard when invalid token used.
* Bugfix: Fix serializer potentially returning `None` object for models that define `__bool__` or `__len__`.
### 2.2.5
**Date**: 26th March 2013
* Serializer support for bulk create and bulk update operations.
* Regression fix: Date and time fields return date/time objects by default. Fixes regressions caused by 2.2.2. See [#743][743] for more details.
* Bugfix: Fix 500 error is OAuth not attempted with OAuthAuthentication class installed.
* `Serializer.save()` now supports arbitrary keyword args which are passed through to the object `.save()` method. Mixins use `force_insert` and `force_update` where appropriate, resulting in one less database query.
### 2.2.4
**Date**: 13th March 2013
* OAuth 2 support.
* OAuth 1.0a support.
* Support X-HTTP-Method-Override header.
* Filtering backends are now applied to the querysets for object lookups as well as lists. (Eg you can use a filtering backend to control which objects should 404)
* Deal with error data nicely when deserializing lists of objects.
* Extra override hook to configure `DjangoModelPermissions` for unauthenticated users.
* Bugfix: Fix regression which caused extra database query on paginated list views.
* Bugfix: Fix pk relationship bug for some types of 1-to-1 relations.
* Bugfix: Workaround for Django bug causing case where `Authtoken` could be registered for cascade delete from `User` even if not installed.
### 2.2.3
**Date**: 7th March 2013
* Bugfix: Fix None values for for `DateField`, `DateTimeField` and `TimeField`.
### 2.2.2
**Date**: 6th March 2013
* Support for custom input and output formats for `DateField`, `DateTimeField` and `TimeField`.
* Cleanup: Request authentication is no longer lazily evaluated, instead authentication is always run, which results in more consistent, obvious behavior. Eg. Supplying bad auth credentials will now always return an error response, even if no permissions are set on the view.
* Bugfix for serializer data being uncacheable with pickle protocol 0.
* Bugfixes for model field validation edge-cases.
* Bugfix for authtoken migration while using a custom user model and south.
### 2.2.1
**Date**: 22nd Feb 2013
* Security fix: Use `defusedxml` package to address XML parsing vulnerabilities.
* Raw data tab added to browsable API. (Eg. Allow for JSON input.)
* Added TimeField.
* Serializer fields can be mapped to any method that takes no args, or only takes kwargs which have defaults.
* Unicode support for view names/descriptions in browsable API.
* Bugfix: request.DATA should return an empty `QueryDict` with no data, not `None`.
* Bugfix: Remove unneeded field validation, which caused extra queries.
**Security note**: Following the [disclosure of security vulnerabilities][defusedxml-announce] in Python's XML parsing libraries, use of the `XMLParser` class now requires the `defusedxml` package to be installed.
The security vulnerabilities only affect APIs which use the `XMLParser` class, by enabling it in any views, or by having it set in the `DEFAULT_PARSER_CLASSES` setting. Note that the `XMLParser` class is not enabled by default, so this change should affect a minority of users.
### 2.2.0
**Date**: 13th Feb 2013
* Python 3 support.
* Added a `post_save()` hook to the generic views.
* Allow serializers to handle dicts as well as objects.
* Deprecate `ManyRelatedField()` syntax in favor of `RelatedField(many=True)`
* Deprecate `null=True` on relations in favor of `required=False`.
* Deprecate `blank=True` on CharFields, just use `required=False`.
* Deprecate optional `obj` argument in permissions checks in favor of `has_object_permission`.
* Deprecate implicit hyperlinked relations behavior.
* Bugfix: Fix broken DjangoModelPermissions.
* Bugfix: Allow serializer output to be cached.
* Bugfix: Fix styling on browsable API login.
* Bugfix: Fix issue with deserializing empty to-many relations.
* Bugfix: Ensure model field validation is still applied for ModelSerializer subclasses with an custom `.restore_object()` method.
**Note**: See the [2.2 announcement][2.2-announcement] for full details.
---
## 2.1.x series
### 2.1.17
**Date**: 26th Jan 2013
* Support proper 401 Unauthorized responses where appropriate, instead of always using 403 Forbidden.
* Support json encoding of timedelta objects.
* `format_suffix_patterns()` now supports `include` style URL patterns.
* Bugfix: Fix issues with custom pagination serializers.
* Bugfix: Nested serializers now accept `source='*'` argument.
* Bugfix: Return proper validation errors when incorrect types supplied for relational fields.
* Bugfix: Support nullable FKs with `SlugRelatedField`.
* Bugfix: Don't call custom validation methods if the field has an error.
**Note**: If the primary authentication class is `TokenAuthentication` or `BasicAuthentication`, a view will now correctly return 401 responses to unauthenticated access, with an appropriate `WWW-Authenticate` header, instead of 403 responses.
### 2.1.16
**Date**: 14th Jan 2013
* Deprecate `django.utils.simplejson` in favor of Python 2.6's built-in json module.
* Bugfix: `auto_now`, `auto_now_add` and other `editable=False` fields now default to read-only.
* Bugfix: PK fields now only default to read-only if they are an AutoField or if `editable=False`.
* Bugfix: Validation errors instead of exceptions when serializers receive incorrect types.
* Bugfix: Validation errors instead of exceptions when related fields receive incorrect types.
* Bugfix: Handle ObjectDoesNotExist exception when serializing null reverse one-to-one
**Note**: Prior to 2.1.16, The Decimals would render in JSON using floating point if `simplejson` was installed, but otherwise render using string notation. Now that use of `simplejson` has been deprecated, Decimals will consistently render using string notation. See [ticket 582](ticket-582) for more details.
### 2.1.15
**Date**: 3rd Jan 2013
* Added `PATCH` support.
* Added `RetrieveUpdateAPIView`.
* Remove unused internal `save_m2m` flag on `ModelSerializer.save()`.
* Tweak behavior of hyperlinked fields with an explicit format suffix.
* Relation changes are now persisted in `.save()` instead of in `.restore_object()`.
* Bugfix: Fix issue with FileField raising exception instead of validation error when files=None.
* Bugfix: Partial updates should not set default values if field is not included.
### 2.1.14
**Date**: 31st Dec 2012
* Bugfix: ModelSerializers now include reverse FK fields on creation.
* Bugfix: Model fields with `blank=True` are now `required=False` by default.
* Bugfix: Nested serializers now support nullable relationships.
**Note**: From 2.1.14 onwards, relational fields move out of the `fields.py` module and into the new `relations.py` module, in order to separate them from regular data type fields, such as `CharField` and `IntegerField`.
This change will not affect user code, so long as it's following the recommended import style of `from rest_framework import serializers` and referring to fields using the style `serializers.PrimaryKeyRelatedField`.
### 2.1.13
**Date**: 28th Dec 2012
* Support configurable `STATICFILES_STORAGE` storage.
* Bugfix: Related fields now respect the required flag, and may be required=False.
### 2.1.12
**Date**: 21st Dec 2012
* Bugfix: Fix bug that could occur using ChoiceField.
* Bugfix: Fix exception in browsable API on DELETE.
* Bugfix: Fix issue where pk was was being set to a string if set by URL kwarg.
### 2.1.11
**Date**: 17th Dec 2012
* Bugfix: Fix issue with M2M fields in browsable API.
### 2.1.10
**Date**: 17th Dec 2012
* Bugfix: Ensure read-only fields don't have model validation applied.
* Bugfix: Fix hyperlinked fields in paginated results.
### 2.1.9
**Date**: 11th Dec 2012
* Bugfix: Fix broken nested serialization.
* Bugfix: Fix `Meta.fields` only working as tuple not as list.
* Bugfix: Edge case if unnecessarily specifying `required=False` on read only field.
### 2.1.8
**Date**: 8th Dec 2012
* Fix for creating nullable Foreign Keys with `''` as well as `None`.
* Added `null=<bool>` related field option.
### 2.1.7
**Date**: 7th Dec 2012
* Serializers now properly support nullable Foreign Keys.
* Serializer validation now includes model field validation, such as uniqueness constraints.
* Support 'true' and 'false' string values for BooleanField.
* Added pickle support for serialized data.
* Support `source='dotted.notation'` style for nested serializers.
* Make `Request.user` settable.
* Bugfix: Fix `RegexField` to work with `BrowsableAPIRenderer`.
### 2.1.6
**Date**: 23rd Nov 2012
* Bugfix: Unfix DjangoModelPermissions. (I am a doofus.)
### 2.1.5
**Date**: 23rd Nov 2012
* Bugfix: Fix DjangoModelPermissions.
### 2.1.4
**Date**: 22nd Nov 2012
* Support for partial updates with serializers.
* Added `RegexField`.
* Added `SerializerMethodField`.
* Serializer performance improvements.
* Added `obtain_token_view` to get tokens when using `TokenAuthentication`.
* Bugfix: Django 1.5 configurable user support for `TokenAuthentication`.
### 2.1.3
**Date**: 16th Nov 2012
* Added `FileField` and `ImageField`. For use with `MultiPartParser`.
* Added `URLField` and `SlugField`.
* Support for `read_only_fields` on `ModelSerializer` classes.
* Support for clients overriding the pagination page sizes. Use the `PAGINATE_BY_PARAM` setting or set the `paginate_by_param` attribute on a generic view.
* 201 Responses now return a 'Location' header.
* Bugfix: Serializer fields now respect `max_length`.
### 2.1.2
**Date**: 9th Nov 2012
* **Filtering support.**
* Bugfix: Support creation of objects with reverse M2M relations.
### 2.1.1
**Date**: 7th Nov 2012
* Support use of HTML exception templates. Eg. `403.html`
* Hyperlinked fields take optional `slug_field`, `slug_url_kwarg` and `pk_url_kwarg` arguments.
* Bugfix: Deal with optional trailing slashes properly when generating breadcrumbs.
* Bugfix: Make textareas same width as other fields in browsable API.
* Private API change: `.get_serializer` now uses same `instance` and `data` ordering as serializer initialization.
### 2.1.0
**Date**: 5th Nov 2012
* **Serializer `instance` and `data` keyword args have their position swapped.**
* `queryset` argument is now optional on writable model fields.
* Hyperlinked related fields optionally take `slug_field` and `slug_url_kwarg` arguments.
* Support Django's cache framework.
* Minor field improvements. (Don't stringify dicts, more robust many-pk fields.)
* Bugfix: Support choice field in Browsable API.
* Bugfix: Related fields with `read_only=True` do not require a `queryset` argument.
**API-incompatible changes**: Please read [this thread][2.1.0-notes] regarding the `instance` and `data` keyword args before updating to 2.1.0.
---
## 2.0.x series
### 2.0.2
**Date**: 2nd Nov 2012
* Fix issues with pk related fields in the browsable API.
### 2.0.1
**Date**: 1st Nov 2012
* Add support for relational fields in the browsable API.
* Added SlugRelatedField and ManySlugRelatedField.
* If PUT creates an instance return '201 Created', instead of '200 OK'.
### 2.0.0
**Date**: 30th Oct 2012
* **Fix all of the things.** (Well, almost.)
* For more information please see the [2.0 announcement][announcement].
For older release notes, [please see the GitHub repo](old-release-notes).
For older release notes, [please see the version 2.x documentation](old-release-notes).
[cite]: http://www.catb.org/~esr/writings/cathedral-bazaar/cathedral-bazaar/ar01s04.html
[deprecation-policy]: #deprecation-policy
[django-deprecation-policy]: https://docs.djangoproject.com/en/dev/internals/release-process/#internal-release-deprecation-policy
[defusedxml-announce]: http://blog.python.org/2013/02/announcing-defusedxml-fixes-for-xml.html
[2.2-announcement]: 2.2-announcement.md
[2.3-announcement]: 2.3-announcement.md
[743]: https://github.com/tomchristie/django-rest-framework/pull/743
[staticfiles14]: https://docs.djangoproject.com/en/1.4/howto/static-files/#with-a-template-tag
[staticfiles13]: https://docs.djangoproject.com/en/1.3/howto/static-files/#with-a-template-tag
[2.1.0-notes]: https://groups.google.com/d/topic/django-rest-framework/Vv2M0CMY9bg/discussion
[announcement]: rest-framework-2-announcement.md
[ticket-582]: https://github.com/tomchristie/django-rest-framework/issues/582
[rfc-6266]: http://tools.ietf.org/html/rfc6266#section-4.3
[old-release-notes]: https://github.com/tomchristie/django-rest-framework/blob/2.4.4/docs/topics/release-notes.md#04x-series
[old-release-notes]: http://tomchristie.github.io/rest-framework-2-docs/topics/release-notes#24x-series
[3.0.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.1+Release%22
[3.0.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.2+Release%22

View File

@ -92,7 +92,7 @@ Here is the view for an individual snippet, in the `views.py` module.
This should all feel very familiar - it is not a lot different from working with regular Django views.
Notice that we're no longer explicitly tying our requests or responses to a given content type. `request.data` can handle incoming `json` requests, but it can also handle `yaml` and other formats. Similarly we're returning response objects with data, but allowing REST framework to render the response into the correct content type for us.
Notice that we're no longer explicitly tying our requests or responses to a given content type. `request.data` can handle incoming `json` requests, but it can also handle other formats. Similarly we're returning response objects with data, but allowing REST framework to render the response into the correct content type for us.
## Adding optional format suffixes to our URLs

View File

@ -171,6 +171,25 @@ body{
background-attachment: fixed;
}
#main-content h1:first-of-type {
margin-top: 0
}
#main-content h1, #main-content h2 {
font-weight: 300;
margin-top: 20px
}
#main-content h3, #main-content h4, #main-content h5 {
font-weight: 500;
margin-top: 15px
}
#main-content img {
display: block;
margin: 40px auto;
}
/* custom navigation styles */
.navbar .navbar-inner{
@ -239,6 +258,10 @@ body a:hover{
}
}
h1 code, h2 code, h3 code, h4 code, h5 code {
color: #333;
}
/* sticky footer and footer */
html, body {
height: 100%;

View File

@ -32,6 +32,7 @@ pages:
- ['api-guide/throttling.md', 'API Guide', 'Throttling']
- ['api-guide/filtering.md', 'API Guide', 'Filtering']
- ['api-guide/pagination.md', 'API Guide', 'Pagination']
- ['api-guide/versioning.md', 'API Guide', 'Versioning']
- ['api-guide/content-negotiation.md', 'API Guide', 'Content negotiation']
- ['api-guide/metadata.md', 'API Guide', 'Metadata']
- ['api-guide/format-suffixes.md', 'API Guide', 'Format suffixes']
@ -41,6 +42,7 @@ pages:
- ['api-guide/testing.md', 'API Guide', 'Testing']
- ['api-guide/settings.md', 'API Guide', 'Settings']
- ['topics/documenting-your-api.md', 'Topics', 'Documenting your API']
- ['topics/internationalization.md', 'Topics', 'Internationalization']
- ['topics/ajax-csrf-cors.md', 'Topics', 'AJAX, CSRF & CORS']
- ['topics/browser-enhancements.md', 'Topics',]
- ['topics/browsable-api.md', 'Topics', 'The Browsable API']
@ -48,11 +50,7 @@ pages:
- ['topics/third-party-resources.md', 'Topics', 'Third Party Resources']
- ['topics/contributing.md', 'Topics', 'Contributing to REST framework']
- ['topics/project-management.md', 'Topics', 'Project management']
- ['topics/rest-framework-2-announcement.md', 'Topics', '2.0 Announcement']
- ['topics/2.2-announcement.md', 'Topics', '2.2 Announcement']
- ['topics/2.3-announcement.md', 'Topics', '2.3 Announcement']
- ['topics/2.4-announcement.md', 'Topics', '2.4 Announcement']
- ['topics/3.0-announcement.md', 'Topics', '3.0 Announcement']
- ['topics/3.1-announcement.md', 'Topics', '3.1 Announcement']
- ['topics/kickstarter-announcement.md', 'Topics', 'Kickstarter Announcement']
- ['topics/release-notes.md', 'Topics', 'Release Notes']
- ['topics/credits.md', 'Topics', 'Credits']

View File

@ -9,13 +9,8 @@ flake8==2.2.2
# Optional packages
markdown>=2.1.0
PyYAML>=3.10
defusedxml>=0.3
django-guardian==1.2.4
django-filter>=0.9.2
django-oauth-plus>=2.2.1
oauth2>=1.5.211
django-oauth2-provider>=0.2.4
# wheel for PyPI installs
wheel==0.24.0

View File

@ -3,14 +3,10 @@ Provides various authentication policies.
"""
from __future__ import unicode_literals
import base64
from django.contrib.auth import authenticate
from django.core.exceptions import ImproperlyConfigured
from django.middleware.csrf import CsrfViewMiddleware
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from rest_framework import exceptions, HTTP_HEADER_ENCODING
from rest_framework.compat import oauth, oauth_provider, oauth_provider_store
from rest_framework.compat import oauth2_provider, provider_now, check_nonce
from rest_framework.authtoken.models import Token
@ -70,16 +66,16 @@ class BasicAuthentication(BaseAuthentication):
return None
if len(auth) == 1:
msg = 'Invalid basic header. No credentials provided.'
msg = _('Invalid basic header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = 'Invalid basic header. Credentials string should not contain spaces.'
msg = _('Invalid basic header. Credentials string should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)
try:
auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':')
except (TypeError, UnicodeDecodeError):
msg = 'Invalid basic header. Credentials not correctly base64 encoded'
msg = _('Invalid basic header. Credentials not correctly base64 encoded.')
raise exceptions.AuthenticationFailed(msg)
userid, password = auth_parts[0], auth_parts[2]
@ -90,8 +86,13 @@ class BasicAuthentication(BaseAuthentication):
Authenticate the userid and password against username and password.
"""
user = authenticate(username=userid, password=password)
if user is None or not user.is_active:
raise exceptions.AuthenticationFailed('Invalid username/password')
if user is None:
raise exceptions.AuthenticationFailed(_('Invalid username/password.'))
if not user.is_active:
raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))
return (user, None)
def authenticate_header(self, request):
@ -157,10 +158,10 @@ class TokenAuthentication(BaseAuthentication):
return None
if len(auth) == 1:
msg = 'Invalid token header. No credentials provided.'
msg = _('Invalid token header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = 'Invalid token header. Token string should not contain spaces.'
msg = _('Invalid token header. Token string should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)
return self.authenticate_credentials(auth[1])
@ -169,190 +170,12 @@ class TokenAuthentication(BaseAuthentication):
try:
token = self.model.objects.select_related('user').get(key=key)
except self.model.DoesNotExist:
raise exceptions.AuthenticationFailed('Invalid token')
raise exceptions.AuthenticationFailed(_('Invalid token.'))
if not token.user.is_active:
raise exceptions.AuthenticationFailed('User inactive or deleted')
raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))
return (token.user, token)
def authenticate_header(self, request):
return 'Token'
class OAuthAuthentication(BaseAuthentication):
"""
OAuth 1.0a authentication backend using `django-oauth-plus` and `oauth2`.
Note: The `oauth2` package actually provides oauth1.0a support. Urg.
We import it from the `compat` module as `oauth`.
"""
www_authenticate_realm = 'api'
def __init__(self, *args, **kwargs):
super(OAuthAuthentication, self).__init__(*args, **kwargs)
if oauth is None:
raise ImproperlyConfigured(
"The 'oauth2' package could not be imported."
"It is required for use with the 'OAuthAuthentication' class.")
if oauth_provider is None:
raise ImproperlyConfigured(
"The 'django-oauth-plus' package could not be imported."
"It is required for use with the 'OAuthAuthentication' class.")
def authenticate(self, request):
"""
Returns two-tuple of (user, token) if authentication succeeds,
or None otherwise.
"""
try:
oauth_request = oauth_provider.utils.get_oauth_request(request)
except oauth.Error as err:
raise exceptions.AuthenticationFailed(err.message)
if not oauth_request:
return None
oauth_params = oauth_provider.consts.OAUTH_PARAMETERS_NAMES
found = any(param for param in oauth_params if param in oauth_request)
missing = list(param for param in oauth_params if param not in oauth_request)
if not found:
# OAuth authentication was not attempted.
return None
if missing:
# OAuth was attempted but missing parameters.
msg = 'Missing parameters: %s' % (', '.join(missing))
raise exceptions.AuthenticationFailed(msg)
if not self.check_nonce(request, oauth_request):
msg = 'Nonce check failed'
raise exceptions.AuthenticationFailed(msg)
try:
consumer_key = oauth_request.get_parameter('oauth_consumer_key')
consumer = oauth_provider_store.get_consumer(request, oauth_request, consumer_key)
except oauth_provider.store.InvalidConsumerError:
msg = 'Invalid consumer token: %s' % oauth_request.get_parameter('oauth_consumer_key')
raise exceptions.AuthenticationFailed(msg)
if consumer.status != oauth_provider.consts.ACCEPTED:
msg = 'Invalid consumer key status: %s' % consumer.get_status_display()
raise exceptions.AuthenticationFailed(msg)
try:
token_param = oauth_request.get_parameter('oauth_token')
token = oauth_provider_store.get_access_token(request, oauth_request, consumer, token_param)
except oauth_provider.store.InvalidTokenError:
msg = 'Invalid access token: %s' % oauth_request.get_parameter('oauth_token')
raise exceptions.AuthenticationFailed(msg)
try:
self.validate_token(request, consumer, token)
except oauth.Error as err:
raise exceptions.AuthenticationFailed(err.message)
user = token.user
if not user.is_active:
msg = 'User inactive or deleted: %s' % user.username
raise exceptions.AuthenticationFailed(msg)
return (token.user, token)
def authenticate_header(self, request):
"""
If permission is denied, return a '401 Unauthorized' response,
with an appropriate 'WWW-Authenticate' header.
"""
return 'OAuth realm="%s"' % self.www_authenticate_realm
def validate_token(self, request, consumer, token):
"""
Check the token and raise an `oauth.Error` exception if invalid.
"""
oauth_server, oauth_request = oauth_provider.utils.initialize_server_request(request)
oauth_server.verify_request(oauth_request, consumer, token)
def check_nonce(self, request, oauth_request):
"""
Checks nonce of request, and return True if valid.
"""
oauth_nonce = oauth_request['oauth_nonce']
oauth_timestamp = oauth_request['oauth_timestamp']
return check_nonce(request, oauth_request, oauth_nonce, oauth_timestamp)
class OAuth2Authentication(BaseAuthentication):
"""
OAuth 2 authentication backend using `django-oauth2-provider`
"""
www_authenticate_realm = 'api'
allow_query_params_token = settings.DEBUG
def __init__(self, *args, **kwargs):
super(OAuth2Authentication, self).__init__(*args, **kwargs)
if oauth2_provider is None:
raise ImproperlyConfigured(
"The 'django-oauth2-provider' package could not be imported. "
"It is required for use with the 'OAuth2Authentication' class.")
def authenticate(self, request):
"""
Returns two-tuple of (user, token) if authentication succeeds,
or None otherwise.
"""
auth = get_authorization_header(request).split()
if len(auth) == 1:
msg = 'Invalid bearer header. No credentials provided.'
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = 'Invalid bearer header. Token string should not contain spaces.'
raise exceptions.AuthenticationFailed(msg)
if auth and auth[0].lower() == b'bearer':
access_token = auth[1]
elif 'access_token' in request.POST:
access_token = request.POST['access_token']
elif 'access_token' in request.GET and self.allow_query_params_token:
access_token = request.GET['access_token']
else:
return None
return self.authenticate_credentials(request, access_token)
def authenticate_credentials(self, request, access_token):
"""
Authenticate the request, given the access token.
"""
try:
token = oauth2_provider.oauth2.models.AccessToken.objects.select_related('user')
# provider_now switches to timezone aware datetime when
# the oauth2_provider version supports to it.
token = token.get(token=access_token, expires__gt=provider_now())
except oauth2_provider.oauth2.models.AccessToken.DoesNotExist:
raise exceptions.AuthenticationFailed('Invalid token')
user = token.user
if not user.is_active:
msg = 'User inactive or deleted: %s' % user.get_username()
raise exceptions.AuthenticationFailed(msg)
return (user, token)
def authenticate_header(self, request):
"""
Bearer is the only finalized type currently
Check details on the `OAuth2Authentication.authenticate` method
"""
return 'Bearer realm="%s"' % self.www_authenticate_realm

View File

@ -23,7 +23,7 @@ class AuthTokenSerializer(serializers.Serializer):
msg = _('Unable to log in with provided credentials.')
raise exceptions.ValidationError(msg)
else:
msg = _('Must include "username" and "password"')
msg = _('Must include "username" and "password".')
raise exceptions.ValidationError(msg)
attrs['user'] = user

View File

@ -5,15 +5,13 @@ versions of django/python, and compatibility wrappers around optional packages.
# flake8: noqa
from __future__ import unicode_literals
import inspect
from django.core.exceptions import ImproperlyConfigured
from django.conf import settings
from django.utils.encoding import force_text
from django.utils.six.moves.urllib.parse import urlparse as _urlparse
from django.conf import settings
from django.utils import six
import django
import inspect
def unicode_repr(instance):
@ -33,6 +31,13 @@ def unicode_to_repr(value):
return value
def unicode_http_header(value):
# Coerce HTTP header value to unicode.
if isinstance(value, six.binary_type):
return value.decode('iso-8859-1')
return value
def total_seconds(timedelta):
# TimeDelta.total_seconds() is only available in Python 2.7
if hasattr(timedelta, 'total_seconds'):
@ -232,77 +237,13 @@ except ImportError:
apply_markdown = None
# Yaml is optional
try:
import yaml
except ImportError:
yaml = None
# XML is optional
try:
import defusedxml.ElementTree as etree
except ImportError:
etree = None
# OAuth2 is optional
try:
# Note: The `oauth2` package actually provides oauth1.0a support. Urg.
import oauth2 as oauth
except ImportError:
oauth = None
# OAuthProvider is optional
try:
import oauth_provider
from oauth_provider.store import store as oauth_provider_store
# check_nonce's calling signature in django-oauth-plus changes sometime
# between versions 2.0 and 2.2.1
def check_nonce(request, oauth_request, oauth_nonce, oauth_timestamp):
check_nonce_args = inspect.getargspec(oauth_provider_store.check_nonce).args
if 'timestamp' in check_nonce_args:
return oauth_provider_store.check_nonce(
request, oauth_request, oauth_nonce, oauth_timestamp
)
return oauth_provider_store.check_nonce(
request, oauth_request, oauth_nonce
)
except (ImportError, ImproperlyConfigured):
oauth_provider = None
oauth_provider_store = None
check_nonce = None
# OAuth 2 support is optional
try:
import provider as oauth2_provider
from provider import scope as oauth2_provider_scope
from provider import constants as oauth2_constants
if oauth2_provider.__version__ in ('0.2.3', '0.2.4'):
# 0.2.3 and 0.2.4 are supported version that do not support
# timezone aware datetimes
import datetime
provider_now = datetime.datetime.now
else:
# Any other supported version does use timezone aware datetimes
from django.utils.timezone import now as provider_now
except ImportError:
oauth2_provider = None
oauth2_provider_scope = None
oauth2_constants = None
provider_now = None
# `separators` argument to `json.dumps()` differs between 2.x and 3.x
# See: http://bugs.python.org/issue22767
if six.PY3:
SHORT_SEPARATORS = (',', ':')
LONG_SEPARATORS = (', ', ': ')
INDENT_SEPARATORS = (',', ': ')
else:
SHORT_SEPARATORS = (b',', b':')
LONG_SEPARATORS = (b', ', b': ')
INDENT_SEPARATORS = (b',', b': ')

View File

@ -7,8 +7,7 @@ In addition Django's built in 403 and 404 exceptions are handled.
from __future__ import unicode_literals
from django.utils import six
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy
from django.utils.translation import ugettext_lazy as _, ungettext
from rest_framework import status
import math
@ -36,7 +35,7 @@ class APIException(Exception):
Subclasses should provide `.status_code` and `.default_detail` properties.
"""
status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
default_detail = _('A server error occured')
default_detail = _('A server error occurred.')
def __init__(self, detail=None):
if detail is not None:
@ -89,20 +88,25 @@ class PermissionDenied(APIException):
default_detail = _('You do not have permission to perform this action.')
class NotFound(APIException):
status_code = status.HTTP_404_NOT_FOUND
default_detail = _('Not found.')
class MethodNotAllowed(APIException):
status_code = status.HTTP_405_METHOD_NOT_ALLOWED
default_detail = _("Method '%s' not allowed.")
default_detail = _('Method "{method}" not allowed.')
def __init__(self, method, detail=None):
if detail is not None:
self.detail = force_text(detail)
else:
self.detail = force_text(self.default_detail) % method
self.detail = force_text(self.default_detail).format(method=method)
class NotAcceptable(APIException):
status_code = status.HTTP_406_NOT_ACCEPTABLE
default_detail = _('Could not satisfy the request Accept header')
default_detail = _('Could not satisfy the request Accept header.')
def __init__(self, detail=None, available_renderers=None):
if detail is not None:
@ -114,23 +118,22 @@ class NotAcceptable(APIException):
class UnsupportedMediaType(APIException):
status_code = status.HTTP_415_UNSUPPORTED_MEDIA_TYPE
default_detail = _("Unsupported media type '%s' in request.")
default_detail = _('Unsupported media type "{media_type}" in request.')
def __init__(self, media_type, detail=None):
if detail is not None:
self.detail = force_text(detail)
else:
self.detail = force_text(self.default_detail) % media_type
self.detail = force_text(self.default_detail).format(
media_type=media_type
)
class Throttled(APIException):
status_code = status.HTTP_429_TOO_MANY_REQUESTS
default_detail = _('Request was throttled.')
extra_detail = ungettext_lazy(
'Expected available in %(wait)d second.',
'Expected available in %(wait)d seconds.',
'wait'
)
extra_detail_singular = 'Expected available in {wait} second.'
extra_detail_plural = 'Expected available in {wait} seconds.'
def __init__(self, wait=None, detail=None):
if detail is not None:
@ -142,6 +145,8 @@ class Throttled(APIException):
self.wait = None
else:
self.wait = math.ceil(wait)
self.detail += ' ' + force_text(
self.extra_detail % {'wait': self.wait}
)
self.detail += ' ' + force_text(ungettext(
self.extra_detail_singular.format(wait=self.wait),
self.extra_detail_plural.format(wait=self.wait),
self.wait
))

View File

@ -484,7 +484,7 @@ class Field(object):
class BooleanField(Field):
default_error_messages = {
'invalid': _('`{input}` is not a valid boolean.')
'invalid': _('"{input}" is not a valid boolean.')
}
default_empty_html = False
initial = False
@ -512,7 +512,7 @@ class BooleanField(Field):
class NullBooleanField(Field):
default_error_messages = {
'invalid': _('`{input}` is not a valid boolean.')
'invalid': _('"{input}" is not a valid boolean.')
}
initial = None
TRUE_VALUES = set(('t', 'T', 'true', 'True', 'TRUE', '1', 1, True))
@ -555,6 +555,7 @@ class CharField(Field):
def __init__(self, **kwargs):
self.allow_blank = kwargs.pop('allow_blank', False)
self.trim_whitespace = kwargs.pop('trim_whitespace', True)
max_length = kwargs.pop('max_length', None)
min_length = kwargs.pop('min_length', None)
super(CharField, self).__init__(**kwargs)
@ -576,7 +577,8 @@ class CharField(Field):
return super(CharField, self).run_validation(data)
def to_internal_value(self, data):
return six.text_type(data)
value = six.text_type(data)
return value.strip() if self.trim_whitespace else value
def to_representation(self, value):
return six.text_type(value)
@ -592,12 +594,6 @@ class EmailField(CharField):
validator = EmailValidator(message=self.error_messages['invalid'])
self.validators.append(validator)
def to_internal_value(self, data):
return six.text_type(data).strip()
def to_representation(self, value):
return six.text_type(value).strip()
class RegexField(CharField):
default_error_messages = {
@ -612,7 +608,7 @@ class RegexField(CharField):
class SlugField(CharField):
default_error_messages = {
'invalid': _("Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens.")
'invalid': _('Enter a valid "slug" consisting of letters, numbers, underscores or hyphens.')
}
def __init__(self, **kwargs):
@ -624,7 +620,7 @@ class SlugField(CharField):
class URLField(CharField):
default_error_messages = {
'invalid': _("Enter a valid URL.")
'invalid': _('Enter a valid URL.')
}
def __init__(self, **kwargs):
@ -657,7 +653,7 @@ class IntegerField(Field):
'invalid': _('A valid integer is required.'),
'max_value': _('Ensure this value is less than or equal to {max_value}.'),
'min_value': _('Ensure this value is greater than or equal to {min_value}.'),
'max_string_length': _('String value too large')
'max_string_length': _('String value too large.')
}
MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs.
@ -688,10 +684,10 @@ class IntegerField(Field):
class FloatField(Field):
default_error_messages = {
'invalid': _("A valid number is required."),
'invalid': _('A valid number is required.'),
'max_value': _('Ensure this value is less than or equal to {max_value}.'),
'min_value': _('Ensure this value is greater than or equal to {min_value}.'),
'max_string_length': _('String value too large')
'max_string_length': _('String value too large.')
}
MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs.
@ -727,7 +723,7 @@ class DecimalField(Field):
'max_digits': _('Ensure that there are no more than {max_digits} digits in total.'),
'max_decimal_places': _('Ensure that there are no more than {max_decimal_places} decimal places.'),
'max_whole_digits': _('Ensure that there are no more than {max_whole_digits} digits before the decimal point.'),
'max_string_length': _('String value too large')
'max_string_length': _('String value too large.')
}
MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs.
@ -810,7 +806,7 @@ class DecimalField(Field):
class DateTimeField(Field):
default_error_messages = {
'invalid': _('Datetime has wrong format. Use one of these formats instead: {format}'),
'invalid': _('Datetime has wrong format. Use one of these formats instead: {format}.'),
'date': _('Expected a datetime but got a date.'),
}
format = api_settings.DATETIME_FORMAT
@ -875,7 +871,7 @@ class DateTimeField(Field):
class DateField(Field):
default_error_messages = {
'invalid': _('Date has wrong format. Use one of these formats instead: {format}'),
'invalid': _('Date has wrong format. Use one of these formats instead: {format}.'),
'datetime': _('Expected a date but got a datetime.'),
}
format = api_settings.DATE_FORMAT
@ -933,7 +929,7 @@ class DateField(Field):
class TimeField(Field):
default_error_messages = {
'invalid': _('Time has wrong format. Use one of these formats instead: {format}'),
'invalid': _('Time has wrong format. Use one of these formats instead: {format}.'),
}
format = api_settings.TIME_FORMAT
input_formats = api_settings.TIME_INPUT_FORMATS
@ -989,7 +985,7 @@ class TimeField(Field):
class ChoiceField(Field):
default_error_messages = {
'invalid_choice': _('`{input}` is not a valid choice.')
'invalid_choice': _('"{input}" is not a valid choice.')
}
def __init__(self, choices, **kwargs):
@ -1033,8 +1029,8 @@ class ChoiceField(Field):
class MultipleChoiceField(ChoiceField):
default_error_messages = {
'invalid_choice': _('`{input}` is not a valid choice.'),
'not_a_list': _('Expected a list of items but got type `{input_type}`.')
'invalid_choice': _('"{input}" is not a valid choice.'),
'not_a_list': _('Expected a list of items but got type "{input_type}".')
}
default_empty_html = []
@ -1064,10 +1060,10 @@ class MultipleChoiceField(ChoiceField):
class FileField(Field):
default_error_messages = {
'required': _("No file was submitted."),
'invalid': _("The submitted data was not a file. Check the encoding type on the form."),
'no_name': _("No filename could be determined."),
'empty': _("The submitted file is empty."),
'required': _('No file was submitted.'),
'invalid': _('The submitted data was not a file. Check the encoding type on the form.'),
'no_name': _('No filename could be determined.'),
'empty': _('The submitted file is empty.'),
'max_length': _('Ensure this filename has at most {max_length} characters (it has {length}).'),
}
use_url = api_settings.UPLOADED_FILES_USE_URL
@ -1110,8 +1106,7 @@ class FileField(Field):
class ImageField(FileField):
default_error_messages = {
'invalid_image': _(
'Upload a valid image. The file you uploaded was either not an '
'image or a corrupted image.'
'Upload a valid image. The file you uploaded was either not an image or a corrupted image.'
),
}
@ -1149,7 +1144,7 @@ class ListField(Field):
child = _UnvalidatedField()
initial = []
default_error_messages = {
'not_a_list': _('Expected a list of items but got type `{input_type}`')
'not_a_list': _('Expected a list of items but got type "{input_type}".')
}
def __init__(self, *args, **kwargs):
@ -1186,7 +1181,7 @@ class DictField(Field):
child = _UnvalidatedField()
initial = []
default_error_messages = {
'not_a_dict': _('Expected a dictionary of items but got type `{input_type}`')
'not_a_dict': _('Expected a dictionary of items but got type "{input_type}".')
}
def __init__(self, *args, **kwargs):
@ -1197,9 +1192,9 @@ class DictField(Field):
def get_value(self, dictionary):
# We override the default field access in order to support
# lists in HTML forms.
# dictionaries in HTML forms.
if html.is_html_input(dictionary):
return html.parse_html_list(dictionary, prefix=self.field_name)
return html.parse_html_dict(dictionary, prefix=self.field_name)
return dictionary.get(self.field_name, empty)
def to_internal_value(self, data):

View File

@ -114,7 +114,7 @@ class OrderingFilter(BaseFilterBackend):
ordering_param = api_settings.ORDERING_PARAM
ordering_fields = None
def get_ordering(self, request):
def get_ordering(self, request, queryset, view):
"""
Ordering is set by a comma delimited ?ordering=... query parameter.
@ -124,7 +124,13 @@ class OrderingFilter(BaseFilterBackend):
"""
params = request.query_params.get(self.ordering_param)
if params:
return [param.strip() for param in params.split(',')]
fields = [param.strip() for param in params.split(',')]
ordering = self.remove_invalid_fields(queryset, fields, view)
if ordering:
return ordering
# No ordering was included, or all the ordering fields were invalid
return self.get_default_ordering(view)
def get_default_ordering(self, view):
ordering = getattr(view, 'ordering', None)
@ -132,7 +138,7 @@ class OrderingFilter(BaseFilterBackend):
return (ordering,)
return ordering
def remove_invalid_fields(self, queryset, ordering, view):
def remove_invalid_fields(self, queryset, fields, view):
valid_fields = getattr(view, 'ordering_fields', self.ordering_fields)
if valid_fields is None:
@ -152,18 +158,10 @@ class OrderingFilter(BaseFilterBackend):
valid_fields = [field.name for field in queryset.model._meta.fields]
valid_fields += queryset.query.aggregates.keys()
return [term for term in ordering 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):
ordering = self.get_ordering(request)
if ordering:
# Skip any incorrect parameters
ordering = self.remove_invalid_fields(queryset, ordering, view)
if not ordering:
# Use 'ordering' attribute by default
ordering = self.get_default_ordering(view)
ordering = self.get_ordering(request, queryset, view)
if ordering:
return queryset.order_by(*ordering)

View File

@ -2,29 +2,13 @@
Generic views that provide commonly needed behaviour.
"""
from __future__ import unicode_literals
from django.core.paginator import Paginator, InvalidPage
from django.db.models.query import QuerySet
from django.http import Http404
from django.shortcuts import get_object_or_404 as _get_object_or_404
from django.utils import six
from django.utils.translation import ugettext as _
from rest_framework import views, mixins
from rest_framework.settings import api_settings
def strict_positive_int(integer_string, cutoff=None):
"""
Cast a string to a strictly positive integer.
"""
ret = int(integer_string)
if ret <= 0:
raise ValueError()
if cutoff:
ret = min(ret, cutoff)
return ret
def get_object_or_404(queryset, *filter_args, **filter_kwargs):
"""
Same as Django's standard shortcut, but make sure to also raise 404
@ -40,7 +24,6 @@ class GenericAPIView(views.APIView):
"""
Base class for all other generic views.
"""
# You'll need to either set these attributes,
# or override `get_queryset()`/`get_serializer_class()`.
# If you are overriding a view method, it is important that you call
@ -50,146 +33,16 @@ class GenericAPIView(views.APIView):
queryset = None
serializer_class = None
# If you want to use object lookups other than pk, set this attribute.
# If you want to use object lookups other than pk, set 'lookup_field'.
# For more complex lookup requirements override `get_object()`.
lookup_field = 'pk'
lookup_url_kwarg = None
# Pagination settings
paginate_by = api_settings.PAGINATE_BY
paginate_by_param = api_settings.PAGINATE_BY_PARAM
max_paginate_by = api_settings.MAX_PAGINATE_BY
pagination_serializer_class = api_settings.DEFAULT_PAGINATION_SERIALIZER_CLASS
page_kwarg = 'page'
# The filter backend classes to use for queryset filtering
filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
# The following attribute may be subject to change,
# and should be considered private API.
paginator_class = Paginator
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
return {
'request': self.request,
'format': self.format_kwarg,
'view': self
}
def get_serializer(self, *args, **kwargs):
"""
Return the serializer instance that should be used for validating and
deserializing input, and for serializing output.
"""
serializer_class = self.get_serializer_class()
kwargs['context'] = self.get_serializer_context()
return serializer_class(*args, **kwargs)
def get_pagination_serializer(self, page):
"""
Return a serializer instance to use with paginated data.
"""
class SerializerClass(self.pagination_serializer_class):
class Meta:
object_serializer_class = self.get_serializer_class()
pagination_serializer_class = SerializerClass
context = self.get_serializer_context()
return pagination_serializer_class(instance=page, context=context)
def paginate_queryset(self, queryset):
"""
Paginate a queryset if required, either returning a page object,
or `None` if pagination is not configured for this view.
"""
page_size = self.get_paginate_by()
if not page_size:
return None
paginator = self.paginator_class(queryset, page_size)
page_kwarg = self.kwargs.get(self.page_kwarg)
page_query_param = self.request.query_params.get(self.page_kwarg)
page = page_kwarg or page_query_param or 1
try:
page_number = paginator.validate_number(page)
except InvalidPage:
if page == 'last':
page_number = paginator.num_pages
else:
raise Http404(_("Page is not 'last', nor can it be converted to an int."))
try:
page = paginator.page(page_number)
except InvalidPage as exc:
error_format = _('Invalid page (%(page_number)s): %(message)s')
raise Http404(error_format % {
'page_number': page_number,
'message': six.text_type(exc)
})
return page
def filter_queryset(self, queryset):
"""
Given a queryset, filter it with whichever filter backend is in use.
You are unlikely to want to override this method, although you may need
to call it either from a list view, or from a custom `get_object`
method if you want to apply the configured filtering backend to the
default queryset.
"""
for backend in self.get_filter_backends():
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset
def get_filter_backends(self):
"""
Returns the list of filter backends that this view requires.
"""
return list(self.filter_backends)
# The following methods provide default implementations
# that you may want to override for more complex cases.
def get_paginate_by(self):
"""
Return the size of pages to use with pagination.
If `PAGINATE_BY_PARAM` is set it will attempt to get the page size
from a named query parameter in the url, eg. ?page_size=100
Otherwise defaults to using `self.paginate_by`.
"""
if self.paginate_by_param:
try:
return strict_positive_int(
self.request.query_params[self.paginate_by_param],
cutoff=self.max_paginate_by
)
except (KeyError, ValueError):
pass
return self.paginate_by
def get_serializer_class(self):
"""
Return the class to use for the serializer.
Defaults to using `self.serializer_class`.
You may want to override this if you need to provide different
serializations depending on the incoming request.
(Eg. admins get full serialization, others get basic serialization)
"""
assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method."
% self.__class__.__name__
)
return self.serializer_class
# The style to use for queryset pagination.
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
def get_queryset(self):
"""
@ -246,6 +99,83 @@ class GenericAPIView(views.APIView):
return obj
def get_serializer(self, *args, **kwargs):
"""
Return the serializer instance that should be used for validating and
deserializing input, and for serializing output.
"""
serializer_class = self.get_serializer_class()
kwargs['context'] = self.get_serializer_context()
return serializer_class(*args, **kwargs)
def get_serializer_class(self):
"""
Return the class to use for the serializer.
Defaults to using `self.serializer_class`.
You may want to override this if you need to provide different
serializations depending on the incoming request.
(Eg. admins get full serialization, others get basic serialization)
"""
assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method."
% self.__class__.__name__
)
return self.serializer_class
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
return {
'request': self.request,
'format': self.format_kwarg,
'view': self
}
def filter_queryset(self, queryset):
"""
Given a queryset, filter it with whichever filter backend is in use.
You are unlikely to want to override this method, although you may need
to call it either from a list view, or from a custom `get_object`
method if you want to apply the configured filtering backend to the
default queryset.
"""
for backend in list(self.filter_backends):
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset
@property
def paginator(self):
"""
The paginator instance associated with the view, or `None`.
"""
if not hasattr(self, '_paginator'):
if self.pagination_class is None:
self._paginator = None
else:
self._paginator = self.pagination_class()
return self._paginator
def paginate_queryset(self, queryset):
"""
Return a single page of results, or `None` if pagination is disabled.
"""
if self.paginator is None:
return None
return self.paginator.paginate_queryset(queryset, self.request, view=self)
def get_paginated_response(self, data):
"""
Return a paginated style `Response` object for the given output data.
"""
assert self.paginator is not None
return self.paginator.get_paginated_response(data)
# Concrete view classes that provide method handlers
# by composing the mixin classes with the base view.

Binary file not shown.

View File

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

Binary file not shown.

View File

@ -0,0 +1,325 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Jirka Vejrazka <Jirka.Vejrazka@gmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n"
"Language-Team: Czech (http://www.transifex.com/projects/p/django-rest-framework/language/cs/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: cs\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: authentication.py:69
msgid "Invalid basic header. No credentials provided."
msgstr "Chybná hlavička. Nebyly poskytnuty přihlašovací údaje."
#: authentication.py:72
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "Chybná hlavička. Přihlašovací údaje by neměly obsahovat mezery."
#: authentication.py:78
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "Chybná hlavička. Přihlašovací údaje nebyly správně zakódovány pomocí base64."
#: authentication.py:90
msgid "Invalid username/password."
msgstr "Chybné uživatelské jméno nebo heslo."
#: authentication.py:156
msgid "Invalid token header. No credentials provided."
msgstr "Chybná hlavička tokenu. Nebyly zadány přihlašovací údaje."
#: authentication.py:159
msgid "Invalid token header. Token string should not contain spaces."
msgstr "Chybná hlavička tokenu. Přihlašovací údaje by neměly obsahovat mezery."
#: authentication.py:168
msgid "Invalid token."
msgstr "Chybný token."
#: authentication.py:171
msgid "User inactive or deleted."
msgstr "Uživatelský účet je neaktivní nebo byl smazán."
#: exceptions.py:38
msgid "A server error occurred."
msgstr "Chyba na straně serveru."
#: exceptions.py:73
msgid "Malformed request."
msgstr "Neplatný formát požadavku."
#: exceptions.py:78
msgid "Incorrect authentication credentials."
msgstr "Chybné přihlašovací údaje."
#: exceptions.py:83
msgid "Authentication credentials were not provided."
msgstr "Přihlašovací údaje nebyly zadány."
#: exceptions.py:88
msgid "You do not have permission to perform this action."
msgstr "K této akci nemáte oprávnění."
#: exceptions.py:93
msgid "Not found."
msgstr "Nenalezeno."
#: exceptions.py:98
msgid "Method \"{method}\" not allowed."
msgstr "Metoda \"{method}\" není povolena."
#: exceptions.py:109
msgid "Could not satisfy the request Accept header."
msgstr "Nelze vyhovět požadavku v hlavičce Accept."
#: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request."
msgstr "Nepodporovaný media type \"{media_type}\" v požadavku."
#: exceptions.py:134
msgid "Request was throttled."
msgstr "Pořadavek byl limitován kvůli omezení počtu požadavků za časovou periodu."
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155
msgid "This field is required."
msgstr "Toto pole je vyžadováno."
#: fields.py:154
msgid "This field may not be null."
msgstr "Toto pole nesmí být prázdné (null)."
#: fields.py:487 fields.py:515
msgid "\"{input}\" is not a valid boolean."
msgstr "\"{input}\" nelze použít jako typ boolean."
#: fields.py:550
msgid "This field may not be blank."
msgstr "Toto pole nesmí být prázdné.."
#: fields.py:551 fields.py:1324
msgid "Ensure this field has no more than {max_length} characters."
msgstr "Zkontrolujte, že toto pole není delší než {max_length} znaků."
#: fields.py:552
msgid "Ensure this field has at least {min_length} characters."
msgstr "Zkontrolujte, že toto obsahuje alespoň {min_length} znaků"
#: fields.py:587
msgid "Enter a valid email address."
msgstr "Vložte platnou e-mailovou adresu."
#: fields.py:604
msgid "This value does not match the required pattern."
msgstr "Hodnota v tomto poli neodpovídá požadovanému formátu."
#: fields.py:615
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr "Vložte platnou \"zkrácenou formu\" obsahující pouze malá písmena, čísla, spojovník nebo podtržítko."
#: fields.py:627
msgid "Enter a valid URL."
msgstr "Vložte platný odkaz."
#: fields.py:638
msgid "\"{value}\" is not a valid UUID."
msgstr ""
#: fields.py:657
msgid "A valid integer is required."
msgstr "Je vyžadováno číslo."
#: fields.py:658 fields.py:692 fields.py:725
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "Zkontrolujte, že hodnota je menší nebo rovna {max_value}."
#: fields.py:659 fields.py:693 fields.py:726
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "Zkontrolujte, že hodnota je větší nebo rovna {min_value}."
#: fields.py:660 fields.py:694 fields.py:730
msgid "String value too large."
msgstr "Řetězec je příliš dlouhý"
#: fields.py:691 fields.py:724
msgid "A valid number is required."
msgstr "Je vyžadováno číslo."
#: fields.py:727
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "Zkontrolujte, že číslo neobsahuje více než {max_digits} čislic."
#: fields.py:728
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "Zkontrolujte, že číslo nemá více než {max_decimal_places} desetinných míst."
#: fields.py:729
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr "Zkontrolujte, že číslo neobsahuje více než {max_whole_digits} čislic před desetinnou čárkou."
#: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "Chybný formát data a času. Použijte jeden z těchto formátů: {format}."
#: fields.py:814
msgid "Expected a datetime but got a date."
msgstr "Bylo zadáno pouze datum místo data a času."
#: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "Chybný formát data. Použijte jeden z těchto formátů: {format}."
#: fields.py:879
msgid "Expected a date but got a datetime."
msgstr "Bylo zadáno datum a čas, místo samotného data."
#: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "Chybný formát času. Použijte jeden z těchto formátů: {format}."
#: fields.py:992 fields.py:1036
msgid "\"{input}\" is not a valid choice."
msgstr "\"{input}\" není platnou možností."
#: fields.py:1037 fields.py:1151 serializers.py:482
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "Byl očekáván seznam položek ale nalezen \"{input_type}\"."
#: fields.py:1067
msgid "No file was submitted."
msgstr "Nebyl zaslán žádný soubor."
#: fields.py:1068
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr "Zaslaná data neobsahují soubor. Zkontrolujte typ kódování ve formuláři."
#: fields.py:1069
msgid "No filename could be determined."
msgstr "Nebylo možno zjistit jméno souboru."
#: fields.py:1070
msgid "The submitted file is empty."
msgstr "Zaslaný soubor je prázdný."
#: fields.py:1071
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "Zajistěte, aby jméno souboru obsahovalo maximálně {max_length} znaků (teď má {length} znaků)."
#: fields.py:1113
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr "Nahrajte platný obrázek. Nahraný soubor buď není obrázkem, nebo je poškozen."
#: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr ""
#: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}."
msgstr "Chybné čislo stránky \"{page_number}\": {message}."
#: pagination.py:442
msgid "Invalid cursor"
msgstr ""
#: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "Chybný primární klíč \"{pk_value}\" - objekt neexistuje."
#: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "Chybný typ. Byl přijat typ {data_type} místo hodnoty primárního klíče."
#: relations.py:157
msgid "Invalid hyperlink - No URL match."
msgstr "Chybný odkaz - nebyla nalezena žádní shoda."
#: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match."
msgstr "Chybný odkaz - byla nalezena neplatná shoda."
#: relations.py:159
msgid "Invalid hyperlink - Object does not exist."
msgstr "Chybný odkaz - objekt neexistuje."
#: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "Chybný typ. Byl přijat typ {data_type} místo očekávaného odkazu."
#: relations.py:295
msgid "Object with {slug_name}={value} does not exist."
msgstr "Objekt s {slug_name}={value} neexistuje."
#: relations.py:296
msgid "Invalid value."
msgstr "Chybná hodnota."
#: serializers.py:299
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "Chybná data. Byl přijat typ {datatype} místo očakávaného slovníku."
#: validators.py:22
msgid "This field must be unique."
msgstr "Tato položka musí být unikátní."
#: validators.py:76
msgid "The fields {field_names} must make a unique set."
msgstr "Položka {field_names} musí tvořit unikátní množinu."
#: validators.py:219
msgid "This field must be unique for the \"{date_field}\" date."
msgstr "Tato položka musí být pro datum \"{date_field}\" unikátní."
#: validators.py:234
msgid "This field must be unique for the \"{date_field}\" month."
msgstr "Tato položka musí být pro měsíc \"{date_field}\" unikátní."
#: validators.py:247
msgid "This field must be unique for the \"{date_field}\" year."
msgstr "Tato položka musí být pro rok \"{date_field}\" unikátní."
#: versioning.py:39
msgid "Invalid version in \"Accept\" header."
msgstr "Chybné číslo verze v hlavičce Accept"
#: versioning.py:70 versioning.py:112
msgid "Invalid version in URL path."
msgstr "Chybné číslo verze v odkazu."
#: versioning.py:138
msgid "Invalid version in hostname."
msgstr "Chybné číslo verze v hostname."
#: versioning.py:160
msgid "Invalid version in query parameter."
msgstr "Chybné čislo verze v URL parametru."
#: authtoken/serializers.py:20
msgid "User account is disabled."
msgstr "Uživatelský účet je zamčen."
#: authtoken/serializers.py:23
msgid "Unable to log in with provided credentials."
msgstr "Se zadanými údaji nebylo možné se přihlásit."
#: authtoken/serializers.py:26
msgid "Must include \"username\" and \"password\"."
msgstr "Musí obsahovat \"uživatelské jméno! a \"heslo\"."

Binary file not shown.

View File

@ -0,0 +1,325 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Mikkel Munch Mortensen <3xm@detfalskested.dk>, 2015
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n"
"Language-Team: Danish (http://www.transifex.com/projects/p/django-rest-framework/language/da/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: da\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: authentication.py:69
msgid "Invalid basic header. No credentials provided."
msgstr "Ugyldig basic header. Ingen legitimation angivet."
#: authentication.py:72
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "Ugyldig basic header. Legitimationsstrenge må ikke indeholde mellemrum."
#: authentication.py:78
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "Ugyldig basic header. Legitimationen er ikke base64 encoded på korrekt vis."
#: authentication.py:90
msgid "Invalid username/password."
msgstr "Ugyldigt brugernavn/kodeord."
#: authentication.py:156
msgid "Invalid token header. No credentials provided."
msgstr "Ugyldig token header."
#: authentication.py:159
msgid "Invalid token header. Token string should not contain spaces."
msgstr "Ugyldig token header. Token-strenge må ikke indeholde mellemrum."
#: authentication.py:168
msgid "Invalid token."
msgstr "Ugyldigt token."
#: authentication.py:171
msgid "User inactive or deleted."
msgstr "Inaktiv eller slettet bruger."
#: exceptions.py:38
msgid "A server error occurred."
msgstr "Der er sket en serverfejl."
#: exceptions.py:73
msgid "Malformed request."
msgstr "Misdannet forespørgsel."
#: exceptions.py:78
msgid "Incorrect authentication credentials."
msgstr "Ugyldig legitimation til autentificering."
#: exceptions.py:83
msgid "Authentication credentials were not provided."
msgstr "Legitimation til autentificering blev ikke angivet."
#: exceptions.py:88
msgid "You do not have permission to perform this action."
msgstr "Du har ikke lov til at udføre denne handling."
#: exceptions.py:93
msgid "Not found."
msgstr "Ikke fundet."
#: exceptions.py:98
msgid "Method \"{method}\" not allowed."
msgstr "Metoden \"{method}\" er ikke tilladt."
#: exceptions.py:109
msgid "Could not satisfy the request Accept header."
msgstr "Kunne ikke efterkomme forespørgslens Accept header."
#: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request."
msgstr "Forespørgslens media type, \"{media_type}\", er ikke understøttet."
#: exceptions.py:134
msgid "Request was throttled."
msgstr "Forespørgslen blev neddroslet."
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155
msgid "This field is required."
msgstr "Dette felt er påkrævet."
#: fields.py:154
msgid "This field may not be null."
msgstr "Dette felt må ikke være null."
#: fields.py:487 fields.py:515
msgid "\"{input}\" is not a valid boolean."
msgstr "\"{input}\" er ikke en tilladt boolsk værdi."
#: fields.py:550
msgid "This field may not be blank."
msgstr "Dette felt må ikke være tomt."
#: fields.py:551 fields.py:1324
msgid "Ensure this field has no more than {max_length} characters."
msgstr "Tjek at dette felt ikke indeholder flere end {max_length} tegn."
#: fields.py:552
msgid "Ensure this field has at least {min_length} characters."
msgstr "Tjek at dette felt indeholder mindst {min_length} tegn."
#: fields.py:587
msgid "Enter a valid email address."
msgstr "Angiv en gyldig e-mailadresse."
#: fields.py:604
msgid "This value does not match the required pattern."
msgstr "Denne værdi passer ikke med det påkrævede mønster."
#: fields.py:615
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr "Indtast en gyldig \"slug\", bestående af bogstaver, tal, bund- og bindestreger."
#: fields.py:627
msgid "Enter a valid URL."
msgstr "Indtast en gyldig URL."
#: fields.py:638
msgid "\"{value}\" is not a valid UUID."
msgstr ""
#: fields.py:657
msgid "A valid integer is required."
msgstr "Et gyldigt heltal er påkrævet."
#: fields.py:658 fields.py:692 fields.py:725
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "Tjek at værdien er mindre end eller lig med {max_value}."
#: fields.py:659 fields.py:693 fields.py:726
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "Tjek at værdien er større end eller lig med {min_value}."
#: fields.py:660 fields.py:694 fields.py:730
msgid "String value too large."
msgstr "Strengværdien er for stor."
#: fields.py:691 fields.py:724
msgid "A valid number is required."
msgstr "Et gyldigt tal er påkrævet."
#: fields.py:727
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "Tjek at der ikke er flere end {max_digits} cifre i alt."
#: fields.py:728
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "Tjek at der ikke er flere end {max_decimal_places} cifre efter kommaet."
#: fields.py:729
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr "Tjek at der ikke er flere end {max_whole_digits} cifre før kommaet."
#: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "Datotid har et forkert format. Brug i stedet et af disse formater: {format}."
#: fields.py:814
msgid "Expected a datetime but got a date."
msgstr "Forventede en datotid, men fik en dato."
#: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "Dato har et forkert format. Brug i stedet et af disse formater: {format}."
#: fields.py:879
msgid "Expected a date but got a datetime."
msgstr "Forventede en dato men fik en datotid."
#: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "Klokkeslæt har forkert format. Brug i stedet et af disse formater: {format}. "
#: fields.py:992 fields.py:1036
msgid "\"{input}\" is not a valid choice."
msgstr "\"{input}\" er ikke et gyldigt valg."
#: fields.py:1037 fields.py:1151 serializers.py:482
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "Forventede en liste, men fik noget af typen \"{input_type}\"."
#: fields.py:1067
msgid "No file was submitted."
msgstr "Ingen medsendt fil."
#: fields.py:1068
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr "Det medsendte data var ikke en fil. Tjek typen af indkodning på formularen."
#: fields.py:1069
msgid "No filename could be determined."
msgstr "Filnavnet kunne ikke afgøres."
#: fields.py:1070
msgid "The submitted file is empty."
msgstr "Den medsendte fil er tom."
#: fields.py:1071
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "Sørg for at filnavnet er højst {max_length} langt (det er {length})."
#: fields.py:1113
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr "Medsend et gyldigt billede. Den medsendte fil var enten ikke et billede eller billedfilen var ødelagt."
#: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr ""
#: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}."
msgstr "Ugyldig side \"{page_number}\": {message}."
#: pagination.py:442
msgid "Invalid cursor"
msgstr ""
#: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "Ugyldig primærnøgle \"{pk_value}\" - objektet findes ikke."
#: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "Ugyldig type. Forventet værdi er primærnøgle, fik {data_type}."
#: relations.py:157
msgid "Invalid hyperlink - No URL match."
msgstr "Ugyldigt hyperlink - intet URL match."
#: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match."
msgstr "Ugyldigt hyperlink - forkert URL match."
#: relations.py:159
msgid "Invalid hyperlink - Object does not exist."
msgstr "Ugyldigt hyperlink - objektet findes ikke."
#: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "Forkert type. Forventede en URL-streng, fik {data_type}."
#: relations.py:295
msgid "Object with {slug_name}={value} does not exist."
msgstr "Object med {slug_name}={value} findes ikke."
#: relations.py:296
msgid "Invalid value."
msgstr "Ugyldig værdi."
#: serializers.py:299
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "Ugyldig data. Forventede en dictionary, men fik {datatype}."
#: validators.py:22
msgid "This field must be unique."
msgstr "Dette felt skal være unikt."
#: validators.py:76
msgid "The fields {field_names} must make a unique set."
msgstr "Felterne {field_names} skal udgøre et unikt sæt."
#: validators.py:219
msgid "This field must be unique for the \"{date_field}\" date."
msgstr "Dette felt skal være unikt for \"{date_field}\"-datoen."
#: validators.py:234
msgid "This field must be unique for the \"{date_field}\" month."
msgstr "Dette felt skal være unikt for \"{date_field}\"-måneden."
#: validators.py:247
msgid "This field must be unique for the \"{date_field}\" year."
msgstr "Dette felt skal være unikt for \"{date_field}\"-året."
#: versioning.py:39
msgid "Invalid version in \"Accept\" header."
msgstr "Ugyldig version i \"Accept\" headeren."
#: versioning.py:70 versioning.py:112
msgid "Invalid version in URL path."
msgstr "Ugyldig version i URL-stien."
#: versioning.py:138
msgid "Invalid version in hostname."
msgstr "Ugyldig version i hostname."
#: versioning.py:160
msgid "Invalid version in query parameter."
msgstr "Ugyldig version i forespørgselsparameteren."
#: authtoken/serializers.py:20
msgid "User account is disabled."
msgstr "Brugerkontoen er deaktiveret."
#: authtoken/serializers.py:23
msgid "Unable to log in with provided credentials."
msgstr "Kunne ikke logge ind med den angivne legitimation."
#: authtoken/serializers.py:26
msgid "Must include \"username\" and \"password\"."
msgstr "Skal indeholde \"username\" og \"password\"."

Binary file not shown.

View File

@ -0,0 +1,325 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Thomas Tanner, 2015
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n"
"Language-Team: German (http://www.transifex.com/projects/p/django-rest-framework/language/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: de\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: authentication.py:69
msgid "Invalid basic header. No credentials provided."
msgstr "Ungültiger basic header. Keine Zugangsdaten angegeben."
#: authentication.py:72
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "Ungültiger basic header. Zugangsdaten sollen keine Leerzeichen enthalten."
#: authentication.py:78
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "Ungültiger basic header. Zugangsdaten sind nicht korrekt mit base64 kodiert."
#: authentication.py:90
msgid "Invalid username/password."
msgstr "Ungültiger Benutzername/Passwort"
#: authentication.py:156
msgid "Invalid token header. No credentials provided."
msgstr "Ungültiger token header. Keine Zugangsdaten angegeben."
#: authentication.py:159
msgid "Invalid token header. Token string should not contain spaces."
msgstr "Ungültiger token header. Zugangsdaten sollen keine Leerzeichen enthalten."
#: authentication.py:168
msgid "Invalid token."
msgstr "Ungültiges Token"
#: authentication.py:171
msgid "User inactive or deleted."
msgstr "Benutzer inaktiv oder gelöscht."
#: exceptions.py:38
msgid "A server error occurred."
msgstr "Ein Serverfehler ist aufgetreten."
#: exceptions.py:73
msgid "Malformed request."
msgstr "Fehlerhafte Anfrage."
#: exceptions.py:78
msgid "Incorrect authentication credentials."
msgstr "Falsche Anmeldedaten."
#: exceptions.py:83
msgid "Authentication credentials were not provided."
msgstr "Anmeldedaten fehlen."
#: exceptions.py:88
msgid "You do not have permission to perform this action."
msgstr "Sie sind nicht berechtigt, diese Aktion durchzuführen."
#: exceptions.py:93
msgid "Not found."
msgstr "Nicht gefunden."
#: exceptions.py:98
msgid "Method \"{method}\" not allowed."
msgstr "Methode \"{method}\" nicht erlaubt."
#: exceptions.py:109
msgid "Could not satisfy the request Accept header."
msgstr "Kann den Accept header der Anfrage nicht erfüllen."
#: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request."
msgstr "Nicht unterstützter Medientyp \"{media_type}\" in der Anfrage."
#: exceptions.py:134
msgid "Request was throttled."
msgstr "Die Anfrage wurde gedrosselt."
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155
msgid "This field is required."
msgstr "Dieses Feld ist erforderlich."
#: fields.py:154
msgid "This field may not be null."
msgstr "Dieses Feld darf nicht Null sein."
#: fields.py:487 fields.py:515
msgid "\"{input}\" is not a valid boolean."
msgstr "\"{input}\" ist kein gültiger Boole'scher Wert."
#: fields.py:550
msgid "This field may not be blank."
msgstr "Dieses Feld darf nicht leer sein."
#: fields.py:551 fields.py:1324
msgid "Ensure this field has no more than {max_length} characters."
msgstr "Stelle sicher, dass dieses Feld nicht mehr als {max_length} Zeichen lang ist."
#: fields.py:552
msgid "Ensure this field has at least {min_length} characters."
msgstr "Stelle sicher, dass dieses Feld mindestens {min_length} Zeichen lang ist."
#: fields.py:587
msgid "Enter a valid email address."
msgstr "Gebe eine gültige E-Mail Adresse an."
#: fields.py:604
msgid "This value does not match the required pattern."
msgstr "Dieser Wert passt nicht zu dem erforderlichen Muster."
#: fields.py:615
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr "Gebe ein gültiges \"slug\" aus Buchstaben, Ziffern, Unterstrichen und Minuszeichen ein."
#: fields.py:627
msgid "Enter a valid URL."
msgstr "Gebe eine gültige URL ein."
#: fields.py:638
msgid "\"{value}\" is not a valid UUID."
msgstr ""
#: fields.py:657
msgid "A valid integer is required."
msgstr "Eine gültige Ganzzahl ist erforderlich."
#: fields.py:658 fields.py:692 fields.py:725
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "Stelle sicher, dass dieser Wert kleiner oder gleich {max_value} ist."
#: fields.py:659 fields.py:693 fields.py:726
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "Stelle sicher, dass dieser Wert größer oder gleich {max_value} ist."
#: fields.py:660 fields.py:694 fields.py:730
msgid "String value too large."
msgstr "Zeichenkette zu lang."
#: fields.py:691 fields.py:724
msgid "A valid number is required."
msgstr "Eine gültige Zahl ist erforderlich."
#: fields.py:727
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "Stelle sicher, dass es insgesamt nicht mehr als {max_digits} Ziffern lang ist."
#: fields.py:728
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "Stelle sicher, dass es nicht mehr als {max_decimal_places} Nachkommastellen lang ist."
#: fields.py:729
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr "Stelle sicher, dass es nicht mehr als {max_whole_places} Stellen vor dem Komma lang ist."
#: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "Datum- und Zeitangabe hat das falsche Format. Nutze stattdessen eines dieser Formate: {format}."
#: fields.py:814
msgid "Expected a datetime but got a date."
msgstr "Erwarte eine Datum- und Zeitangabe, erhielt aber ein Datum."
#: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "Datum hat das falsche Format. Nutze stattdessen eines dieser Formate: {format}."
#: fields.py:879
msgid "Expected a date but got a datetime."
msgstr "Erwarte ein Datum, erhielt aber eine Datum- und Zeitangabe."
#: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "Zeitangabe hat das falsche Format. Nutze stattdessen eines dieser Formate: {format}."
#: fields.py:992 fields.py:1036
msgid "\"{input}\" is not a valid choice."
msgstr "\"{input}\" ist keine gültige Option."
#: fields.py:1037 fields.py:1151 serializers.py:482
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "Erwarte eine Liste von Elementen, erhielt aber den Typ \"{input_type}\"."
#: fields.py:1067
msgid "No file was submitted."
msgstr "Es wurde keine Datei übermittelt."
#: fields.py:1068
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr "Die übermittelten Daten sind keine Datei. Prüfe den Kodierungstyp im Formular."
#: fields.py:1069
msgid "No filename could be determined."
msgstr "Der Dateiname konnte nicht ermittelt werden."
#: fields.py:1070
msgid "The submitted file is empty."
msgstr "Die übermittelte Datei ist leer."
#: fields.py:1071
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr ""
#: fields.py:1113
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr ""
#: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr ""
#: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}."
msgstr ""
#: pagination.py:442
msgid "Invalid cursor"
msgstr ""
#: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr ""
#: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr ""
#: relations.py:157
msgid "Invalid hyperlink - No URL match."
msgstr ""
#: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match."
msgstr ""
#: relations.py:159
msgid "Invalid hyperlink - Object does not exist."
msgstr ""
#: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr ""
#: relations.py:295
msgid "Object with {slug_name}={value} does not exist."
msgstr ""
#: relations.py:296
msgid "Invalid value."
msgstr "Ungültiger Wert."
#: serializers.py:299
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "Ungültige Daten. Dictionary erwartet, aber {datatype} erhalten."
#: validators.py:22
msgid "This field must be unique."
msgstr "Dieses Feld muss eineindeutig sein."
#: validators.py:76
msgid "The fields {field_names} must make a unique set."
msgstr ""
#: validators.py:219
msgid "This field must be unique for the \"{date_field}\" date."
msgstr ""
#: validators.py:234
msgid "This field must be unique for the \"{date_field}\" month."
msgstr ""
#: validators.py:247
msgid "This field must be unique for the \"{date_field}\" year."
msgstr ""
#: versioning.py:39
msgid "Invalid version in \"Accept\" header."
msgstr ""
#: versioning.py:70 versioning.py:112
msgid "Invalid version in URL path."
msgstr ""
#: versioning.py:138
msgid "Invalid version in hostname."
msgstr ""
#: versioning.py:160
msgid "Invalid version in query parameter."
msgstr ""
#: authtoken/serializers.py:20
msgid "User account is disabled."
msgstr "Benutzerkonto ist gesperrt."
#: authtoken/serializers.py:23
msgid "Unable to log in with provided credentials."
msgstr "Kann nicht mit den angegeben Zugangsdaten anmelden."
#: authtoken/serializers.py:26
msgid "Must include \"username\" and \"password\"."
msgstr "\"username\" und \"password\" sind erforderlich."

Binary file not shown.

View File

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

Binary file not shown.

View File

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

Binary file not shown.

View File

@ -0,0 +1,327 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# José Padilla <jpadilla@webapplicate.com>, 2015
# Miguel González <migonzalvar@activitycentral.com>, 2015
# Sergio Infante <rsinfante@gmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n"
"Language-Team: Spanish (http://www.transifex.com/projects/p/django-rest-framework/language/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: authentication.py:69
msgid "Invalid basic header. No credentials provided."
msgstr "Cabecera básica inválida. Las credenciales no fueron suministradas."
#: authentication.py:72
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "Cabecera básica inválida. La cadena con las credenciales no debe contener espacios."
#: authentication.py:78
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "Cabecera básica inválida. Las credenciales incorrectamente codificadas en base64."
#: authentication.py:90
msgid "Invalid username/password."
msgstr "Nombre de usuario/contraseña inválidos."
#: authentication.py:156
msgid "Invalid token header. No credentials provided."
msgstr "Cabecera token inválida. Las credenciales no fueron suministradas."
#: authentication.py:159
msgid "Invalid token header. Token string should not contain spaces."
msgstr "Cabecera token inválida. La cadena token no debe contener espacios."
#: authentication.py:168
msgid "Invalid token."
msgstr "Token inválido."
#: authentication.py:171
msgid "User inactive or deleted."
msgstr "Usuario inactivo o borrado."
#: exceptions.py:38
msgid "A server error occurred."
msgstr "Se ha producido un error en el servidor."
#: exceptions.py:73
msgid "Malformed request."
msgstr "Solicitud con formato incorrecto."
#: exceptions.py:78
msgid "Incorrect authentication credentials."
msgstr "Credenciales de autenticación incorrectas."
#: exceptions.py:83
msgid "Authentication credentials were not provided."
msgstr "Las credenciales de autenticación no se proveyeron."
#: exceptions.py:88
msgid "You do not have permission to perform this action."
msgstr "Usted no tiene permiso para realizar esta acción."
#: exceptions.py:93
msgid "Not found."
msgstr "No encontrado."
#: exceptions.py:98
msgid "Method \"{method}\" not allowed."
msgstr "Método \"{method}\" no permitido."
#: exceptions.py:109
msgid "Could not satisfy the request Accept header."
msgstr "No se ha podido satisfacer la solicitud de cabecera de Accept."
#: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request."
msgstr "Tipo de medio \"{media_type}\" incompatible en la solicitud."
#: exceptions.py:134
msgid "Request was throttled."
msgstr "Solicitud fue regulada (throttled)."
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155
msgid "This field is required."
msgstr "Este campo es requerido."
#: fields.py:154
msgid "This field may not be null."
msgstr "Este campo no puede ser nulo."
#: fields.py:487 fields.py:515
msgid "\"{input}\" is not a valid boolean."
msgstr "\"{input}\" no es un booleano válido."
#: fields.py:550
msgid "This field may not be blank."
msgstr "Este campo no puede estar en blanco."
#: fields.py:551 fields.py:1324
msgid "Ensure this field has no more than {max_length} characters."
msgstr "Asegúrese de que este campo no tenga más de {max_length} caracteres."
#: fields.py:552
msgid "Ensure this field has at least {min_length} characters."
msgstr "Asegúrese de que este campo tenga al menos {min_length} caracteres."
#: fields.py:587
msgid "Enter a valid email address."
msgstr "Introduzca una dirección de correo electrónico válida."
#: fields.py:604
msgid "This value does not match the required pattern."
msgstr "Este valor no coincide con el patrón requerido."
#: fields.py:615
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr "Introduzca un \"slug\" válido consistente en letras, números, guiones o guiones bajos."
#: fields.py:627
msgid "Enter a valid URL."
msgstr "Introduzca una URL válida."
#: fields.py:638
msgid "\"{value}\" is not a valid UUID."
msgstr ""
#: fields.py:657
msgid "A valid integer is required."
msgstr "Introduzca un número entero válido."
#: fields.py:658 fields.py:692 fields.py:725
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "Asegúrese de que este valor es menor o igual a {max_value}."
#: fields.py:659 fields.py:693 fields.py:726
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "Asegúrese de que este valor es mayor o igual a {min_value}."
#: fields.py:660 fields.py:694 fields.py:730
msgid "String value too large."
msgstr "Cadena demasiado larga."
#: fields.py:691 fields.py:724
msgid "A valid number is required."
msgstr "Se requiere un número válido."
#: fields.py:727
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "Asegúrese de que no haya más de {max_digits} dígitos en total."
#: fields.py:728
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "Asegúrese de que no haya más de {max_decimal_places} decimales."
#: fields.py:729
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr "Asegúrese de que no haya más de {max_whole_digits} dígitos en la parte entera."
#: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "Fecha/hora con formato erróneo. Use uno de los siguientes formatos en su lugar: {format}."
#: fields.py:814
msgid "Expected a datetime but got a date."
msgstr "Se esperaba un fecha/hora en vez de una fecha."
#: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "Fecha con formato erróneo. Use uno de los siguientes formatos en su lugar: {format}."
#: fields.py:879
msgid "Expected a date but got a datetime."
msgstr "Se esperaba una fecha en vez de una fecha/hora."
#: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "Hora con formato erróneo. Use uno de los siguientes formatos en su lugar: {format}."
#: fields.py:992 fields.py:1036
msgid "\"{input}\" is not a valid choice."
msgstr "\"{input}\" no es una elección válida."
#: fields.py:1037 fields.py:1151 serializers.py:482
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "Se esperaba una lista de elementos en vez del tipo \"{input_type}\"."
#: fields.py:1067
msgid "No file was submitted."
msgstr "No se envió ningún archivo."
#: fields.py:1068
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr "La información enviada no era un archivo. Compruebe el tipo de codificación del formulario."
#: fields.py:1069
msgid "No filename could be determined."
msgstr "No se pudo determinar un nombre de archivo."
#: fields.py:1070
msgid "The submitted file is empty."
msgstr "El archivo enviado está vació."
#: fields.py:1071
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "Asegúrese de que el nombre de archivo no tenga más de {max_length} caracteres (tiene {length})."
#: fields.py:1113
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr "Adjunte una imagen válida. El archivo adjunto o bien no es una imagen o bien está dañado."
#: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr ""
#: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}."
msgstr "Página \"{page_number}\" inválida: {message}."
#: pagination.py:442
msgid "Invalid cursor"
msgstr ""
#: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "Clave primaria \"{pk_value}\" inválida - objeto no existe."
#: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "Tipo incorrecto. Se esperaba valor de clave primaria y se recibió {data_type}."
#: relations.py:157
msgid "Invalid hyperlink - No URL match."
msgstr "Hiperenlace inválido - No hay URL coincidentes."
#: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match."
msgstr "Hiperenlace inválido - Coincidencia incorrecta de la URL."
#: relations.py:159
msgid "Invalid hyperlink - Object does not exist."
msgstr "Hiperenlace inválido - Objeto no existe."
#: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "Tipo incorrecto. Se esperaba una URL y se recibió {data_type}."
#: relations.py:295
msgid "Object with {slug_name}={value} does not exist."
msgstr "Objeto con {slug_name}={value} no existe."
#: relations.py:296
msgid "Invalid value."
msgstr "Valor inválido."
#: serializers.py:299
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "Datos inválidos. Se esperaba un diccionario pero es un {datatype}."
#: validators.py:22
msgid "This field must be unique."
msgstr "Este campo debe ser único."
#: validators.py:76
msgid "The fields {field_names} must make a unique set."
msgstr "Los campos {field_names} deben formar un conjunto único."
#: validators.py:219
msgid "This field must be unique for the \"{date_field}\" date."
msgstr "Este campo debe ser único para el día \"{date_field}\"."
#: validators.py:234
msgid "This field must be unique for the \"{date_field}\" month."
msgstr "Este campo debe ser único para el mes \"{date_field}\"."
#: validators.py:247
msgid "This field must be unique for the \"{date_field}\" year."
msgstr "Este campo debe ser único para el año \"{date_field}\"."
#: versioning.py:39
msgid "Invalid version in \"Accept\" header."
msgstr "Versión inválida en la cabecera \"Accept\"."
#: versioning.py:70 versioning.py:112
msgid "Invalid version in URL path."
msgstr "Versión inválida en la ruta de la URL."
#: versioning.py:138
msgid "Invalid version in hostname."
msgstr "Versión inválida en el nombre de host."
#: versioning.py:160
msgid "Invalid version in query parameter."
msgstr "Versión inválida en el parámetro de consulta."
#: authtoken/serializers.py:20
msgid "User account is disabled."
msgstr "Cuenta de usuario está deshabilitada."
#: authtoken/serializers.py:23
msgid "Unable to log in with provided credentials."
msgstr "No puede iniciar sesión con las credenciales proporcionadas."
#: authtoken/serializers.py:26
msgid "Must include \"username\" and \"password\"."
msgstr "Debe incluir \"username\" y \"password\"."

Binary file not shown.

View File

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

Binary file not shown.

View File

@ -0,0 +1,326 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Etienne Desgagné <etienne.desgagne@evimbec.ca>, 2015
# Martin Maillard <martin.maillard@gmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n"
"Language-Team: French (http://www.transifex.com/projects/p/django-rest-framework/language/fr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: authentication.py:69
msgid "Invalid basic header. No credentials provided."
msgstr "En-tête « basic » non valide. Informations d'identification non fournies."
#: authentication.py:72
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "En-tête « basic » non valide. Les informations d'identification ne doivent pas contenir d'espaces."
#: authentication.py:78
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "En-tête « basic » non valide. Encodage base64 des informations d'identification incorrect."
#: authentication.py:90
msgid "Invalid username/password."
msgstr "Nom d'utilisateur et/ou mot de passe non valide(s)."
#: authentication.py:156
msgid "Invalid token header. No credentials provided."
msgstr "En-tête « token » non valide. Informations d'identification non fournies."
#: authentication.py:159
msgid "Invalid token header. Token string should not contain spaces."
msgstr "En-tête « token » non valide. Un token ne doit pas contenir d'espaces."
#: authentication.py:168
msgid "Invalid token."
msgstr "Token non valide."
#: authentication.py:171
msgid "User inactive or deleted."
msgstr "Utilisateur inactif ou supprimé."
#: exceptions.py:38
msgid "A server error occurred."
msgstr "Une erreur du serveur est survenue."
#: exceptions.py:73
msgid "Malformed request."
msgstr "Requête malformée"
#: exceptions.py:78
msgid "Incorrect authentication credentials."
msgstr "Informations d'authentification incorrectes."
#: exceptions.py:83
msgid "Authentication credentials were not provided."
msgstr "Informations d'authentification non fournies."
#: exceptions.py:88
msgid "You do not have permission to perform this action."
msgstr "Vous n'avez pas la permission d'effectuer cette action."
#: exceptions.py:93
msgid "Not found."
msgstr ""
#: exceptions.py:98
msgid "Method \"{method}\" not allowed."
msgstr "Méthode \"{method}\" non autorisée."
#: exceptions.py:109
msgid "Could not satisfy the request Accept header."
msgstr ""
#: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request."
msgstr ""
#: exceptions.py:134
msgid "Request was throttled."
msgstr ""
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155
msgid "This field is required."
msgstr "Ce champ est obligatoire."
#: fields.py:154
msgid "This field may not be null."
msgstr "Ce champ ne peut être null."
#: fields.py:487 fields.py:515
msgid "\"{input}\" is not a valid boolean."
msgstr "\"{input}\" n'est pas un booléen valide."
#: fields.py:550
msgid "This field may not be blank."
msgstr "Ce champ ne peut être vide."
#: fields.py:551 fields.py:1324
msgid "Ensure this field has no more than {max_length} characters."
msgstr "Assurez-vous que ce champ comporte au plus {max_length} caractères."
#: fields.py:552
msgid "Ensure this field has at least {min_length} characters."
msgstr "Assurez-vous que ce champ comporte au moins {min_length} caractères."
#: fields.py:587
msgid "Enter a valid email address."
msgstr "Saisissez une adresse email valable."
#: fields.py:604
msgid "This value does not match the required pattern."
msgstr "Cette valeur ne satisfait pas le motif imposé."
#: fields.py:615
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr "Ce champ ne doit contenir que des lettres, des nombres, des tirets bas _ et des traits d'union."
#: fields.py:627
msgid "Enter a valid URL."
msgstr "Saisissez une URL valide."
#: fields.py:638
msgid "\"{value}\" is not a valid UUID."
msgstr ""
#: fields.py:657
msgid "A valid integer is required."
msgstr "Saisissez un nombre entier valide."
#: fields.py:658 fields.py:692 fields.py:725
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "Assurez-vous que cette valeur est inférieure ou égale à {max_value}."
#: fields.py:659 fields.py:693 fields.py:726
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "Assurez-vous que cette valeur est supérieure ou égale à {min_value}."
#: fields.py:660 fields.py:694 fields.py:730
msgid "String value too large."
msgstr "Chaîne de caractères trop longue."
#: fields.py:691 fields.py:724
msgid "A valid number is required."
msgstr "Un nombre valide est requis."
#: fields.py:727
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "Assurez-vous qu'il n'y a pas plus de {max_digits} chiffres au total."
#: fields.py:728
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "Assurez-vous qu'il n'y a pas plus de {max_decimal_places} chiffres après la virgule."
#: fields.py:729
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr "Assurez-vous qu'il n'y a pas plus de {max_whole_digits} chiffres avant la virgule."
#: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr ""
#: fields.py:814
msgid "Expected a datetime but got a date."
msgstr ""
#: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr ""
#: fields.py:879
msgid "Expected a date but got a datetime."
msgstr ""
#: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr ""
#: fields.py:992 fields.py:1036
msgid "\"{input}\" is not a valid choice."
msgstr "\"{input}\" n'est pas un choix valide."
#: fields.py:1037 fields.py:1151 serializers.py:482
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr ""
#: fields.py:1067
msgid "No file was submitted."
msgstr "Aucun fichier n'a été soumis."
#: fields.py:1068
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr "La donnée soumise n'est pas un fichier. Vérifiez le type d'encodage du formulaire."
#: fields.py:1069
msgid "No filename could be determined."
msgstr "Le nom de fichier n'a pu être déterminé."
#: fields.py:1070
msgid "The submitted file is empty."
msgstr "Le fichier soumis est vide."
#: fields.py:1071
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "Assurez-vous que le nom de fichier comporte au plus {max_length} caractères (il en comporte {length})."
#: fields.py:1113
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr "Transférez une image valide. Le fichier que vous avez transféré n'est pas une image, ou il est corrompu."
#: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr ""
#: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}."
msgstr "Page \"{page_number}\" non valide : {message}."
#: pagination.py:442
msgid "Invalid cursor"
msgstr ""
#: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "Clé primaire \"{pk_value}\" non valide - l'objet n'existe pas."
#: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr ""
#: relations.py:157
msgid "Invalid hyperlink - No URL match."
msgstr ""
#: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match."
msgstr ""
#: relations.py:159
msgid "Invalid hyperlink - Object does not exist."
msgstr ""
#: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr ""
#: relations.py:295
msgid "Object with {slug_name}={value} does not exist."
msgstr "L'object avec {slug_name}={value} n'existe pas."
#: relations.py:296
msgid "Invalid value."
msgstr "Valeur non valide."
#: serializers.py:299
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr ""
#: validators.py:22
msgid "This field must be unique."
msgstr "Ce champ doit être unique."
#: validators.py:76
msgid "The fields {field_names} must make a unique set."
msgstr "Les champs {field_names} doivent former un ensemble unique."
#: validators.py:219
msgid "This field must be unique for the \"{date_field}\" date."
msgstr "Ce champ doit être unique pour la date \"{date_field}\"."
#: validators.py:234
msgid "This field must be unique for the \"{date_field}\" month."
msgstr "Ce champ doit être unique pour le mois \"{date_field}\"."
#: validators.py:247
msgid "This field must be unique for the \"{date_field}\" year."
msgstr "Ce champ doit être unique pour l'année \"{date_field}\"."
#: versioning.py:39
msgid "Invalid version in \"Accept\" header."
msgstr "Version non valide dans l'en-tête « Accept »."
#: versioning.py:70 versioning.py:112
msgid "Invalid version in URL path."
msgstr "Version non valide dans l'URL."
#: versioning.py:138
msgid "Invalid version in hostname."
msgstr "Version non valide dans le nom d'hôte."
#: versioning.py:160
msgid "Invalid version in query parameter."
msgstr "Version non valide dans le paramètre de requête."
#: authtoken/serializers.py:20
msgid "User account is disabled."
msgstr "Ce compte est désactivé."
#: authtoken/serializers.py:23
msgid "Unable to log in with provided credentials."
msgstr "Impossible de se connecter avec les informations d'identification fournies."
#: authtoken/serializers.py:26
msgid "Must include \"username\" and \"password\"."
msgstr "\"username\" et \"password\" doivent être inclus."

Binary file not shown.

View File

@ -0,0 +1,325 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Zoltan Szalai <defaultdict@gmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n"
"Language-Team: Hungarian (http://www.transifex.com/projects/p/django-rest-framework/language/hu/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: hu\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: authentication.py:69
msgid "Invalid basic header. No credentials provided."
msgstr "Érvénytelen basic fejlécmező. Nem voltak megadva azonosítók."
#: authentication.py:72
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "Érvénytelen basic fejlécmező. Az azonosító karakterlánc nem tartalmazhat szóközöket."
#: authentication.py:78
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "Érvénytelen basic fejlécmező. Az azonosítók base64 kódolása nem megfelelő."
#: authentication.py:90
msgid "Invalid username/password."
msgstr "Érvénytelen felhasználónév/jelszó."
#: authentication.py:156
msgid "Invalid token header. No credentials provided."
msgstr "Érvénytelen token fejlécmező. Nem voltak megadva azonosítók."
#: authentication.py:159
msgid "Invalid token header. Token string should not contain spaces."
msgstr "Érvénytelen token fejlécmező. A token karakterlánc nem tartalmazhat szóközöket."
#: authentication.py:168
msgid "Invalid token."
msgstr "Érvénytelen token."
#: authentication.py:171
msgid "User inactive or deleted."
msgstr "A felhasználó nincs aktiválva vagy törölve lett."
#: exceptions.py:38
msgid "A server error occurred."
msgstr "Szerver oldali hiba történt."
#: exceptions.py:73
msgid "Malformed request."
msgstr "Hibás kérés."
#: exceptions.py:78
msgid "Incorrect authentication credentials."
msgstr "Hibás azonosítók."
#: exceptions.py:83
msgid "Authentication credentials were not provided."
msgstr "Nem voltak megadva azonosítók."
#: exceptions.py:88
msgid "You do not have permission to perform this action."
msgstr "Nincs jogosultsága a művelet végrehajtásához."
#: exceptions.py:93
msgid "Not found."
msgstr "Nem található."
#: exceptions.py:98
msgid "Method \"{method}\" not allowed."
msgstr "A \"{method}\" metódus nem megengedett."
#: exceptions.py:109
msgid "Could not satisfy the request Accept header."
msgstr "A kérés Accept fejlécmezőjét nem lehetett kiszolgálni."
#: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request."
msgstr "Nem támogatott média típus \"{media_type}\" a kérésben."
#: exceptions.py:134
msgid "Request was throttled."
msgstr "A kérés korlátozva lett."
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155
msgid "This field is required."
msgstr "Ennek a mezőnek a megadása kötelező."
#: fields.py:154
msgid "This field may not be null."
msgstr "Ez a mező nem lehet null értékű."
#: fields.py:487 fields.py:515
msgid "\"{input}\" is not a valid boolean."
msgstr "Az \"{input}\" nem egy érvényes logikai érték."
#: fields.py:550
msgid "This field may not be blank."
msgstr "Ez a mező nem lehet üres."
#: fields.py:551 fields.py:1324
msgid "Ensure this field has no more than {max_length} characters."
msgstr "Bizonyosodjon meg arról, hogy ez a mező legfeljebb {max_length} karakterből áll."
#: fields.py:552
msgid "Ensure this field has at least {min_length} characters."
msgstr "Bizonyosodjon meg arról, hogy ez a mező legalább {min_length} karakterből áll."
#: fields.py:587
msgid "Enter a valid email address."
msgstr "Adjon meg egy érvényes e-mail címet!"
#: fields.py:604
msgid "This value does not match the required pattern."
msgstr "Ez az érték nem illeszkedik a szükséges mintázatra."
#: fields.py:615
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr "Az URL barát cím csak betűket, számokat, aláhúzásokat és kötőjeleket tartalmazhat."
#: fields.py:627
msgid "Enter a valid URL."
msgstr "Adjon meg egy érvényes URL-t!"
#: fields.py:638
msgid "\"{value}\" is not a valid UUID."
msgstr ""
#: fields.py:657
msgid "A valid integer is required."
msgstr "Egy érvényes egész szám megadása szükséges."
#: fields.py:658 fields.py:692 fields.py:725
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "Bizonyosodjon meg arról, hogy ez az érték legfeljebb {max_value}."
#: fields.py:659 fields.py:693 fields.py:726
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "Bizonyosodjon meg arról, hogy ez az érték legalább {min_value}."
#: fields.py:660 fields.py:694 fields.py:730
msgid "String value too large."
msgstr "A karakterlánc túl hosszú."
#: fields.py:691 fields.py:724
msgid "A valid number is required."
msgstr "Egy érvényes szám megadása szükséges."
#: fields.py:727
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "Bizonyosodjon meg arról, hogy a számjegyek száma összesen legfeljebb {max_digits}."
#: fields.py:728
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "Bizonyosodjon meg arról, hogy a tizedes tört törtrészében levő számjegyek száma összesen legfeljebb {max_decimal_places}."
#: fields.py:729
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr "Bizonyosodjon meg arról, hogy a tizedes tört egész részében levő számjegyek száma összesen legfeljebb {max_whole_digits}."
#: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "A dátum formátuma hibás. Használja ezek valamelyikét helyette: {format}."
#: fields.py:814
msgid "Expected a datetime but got a date."
msgstr "Időt is tartalmazó dátum helyett egy időt nem tartalmazó dátum lett elküldve."
#: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "A dátum formátuma hibás. Használja ezek valamelyikét helyette: {format}."
#: fields.py:879
msgid "Expected a date but got a datetime."
msgstr "Időt nem tartalmazó dátum helyett egy időt is tartalmazó dátum lett elküldve."
#: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "Az idő formátuma hibás. Használja ezek valamelyikét helyette: {format}."
#: fields.py:992 fields.py:1036
msgid "\"{input}\" is not a valid choice."
msgstr "Az \"{input}\" nem egy érvényes elem."
#: fields.py:1037 fields.py:1151 serializers.py:482
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "Elemek listája helyett \"{input_type}\" lett elküldve."
#: fields.py:1067
msgid "No file was submitted."
msgstr "Semmilyen fájl sem került feltöltésre."
#: fields.py:1068
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr "Az elküldött adat nem egy fájl volt. Ellenőrizze a kódolás típusát az űrlapon!"
#: fields.py:1069
msgid "No filename could be determined."
msgstr "A fájlnév nem megállapítható."
#: fields.py:1070
msgid "The submitted file is empty."
msgstr "A küldött fájl üres."
#: fields.py:1071
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "Bizonyosodjon meg arról, hogy a fájlnév legfeljebb {max_length} karakterből áll (jelenlegi hossza: {length})."
#: fields.py:1113
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr "Töltsön fel egy érvényes képfájlt! A feltöltött fájl nem kép volt, vagy megsérült."
#: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr ""
#: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}."
msgstr "Érvénytelen oldal \"{page_number}\": {message}."
#: pagination.py:442
msgid "Invalid cursor"
msgstr ""
#: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "Érvénytelen pk \"{pk_value}\" - az objektum nem létezik."
#: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "Helytelen típus. pk érték helyett {data_type} lett elküldve."
#: relations.py:157
msgid "Invalid hyperlink - No URL match."
msgstr "Érvénytelen link - Nem illeszkedő URL."
#: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match."
msgstr "Érvénytelen link. - Eltérő URL illeszkedés."
#: relations.py:159
msgid "Invalid hyperlink - Object does not exist."
msgstr "Érvénytelen link - Az objektum nem létezik."
#: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "Helytelen típus. URL karakterlánc helyett {data_type} lett elküldve."
#: relations.py:295
msgid "Object with {slug_name}={value} does not exist."
msgstr "Nem létezik olyan objektum, amelynél {slug_name}={value}."
#: relations.py:296
msgid "Invalid value."
msgstr "Érvénytelen érték."
#: serializers.py:299
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "Érvénytelen adat. Egy dictionary helyett {datatype} lett elküldve."
#: validators.py:22
msgid "This field must be unique."
msgstr "Ennek a mezőnek egyedinek kell lennie."
#: validators.py:76
msgid "The fields {field_names} must make a unique set."
msgstr "A {field_names} mezőnevek nem tartalmazhatnak duplikátumot."
#: validators.py:219
msgid "This field must be unique for the \"{date_field}\" date."
msgstr "A mezőnek egyedinek kell lennie a \"{date_field}\" dátumra."
#: validators.py:234
msgid "This field must be unique for the \"{date_field}\" month."
msgstr "A mezőnek egyedinek kell lennie a \"{date_field}\" hónapra."
#: validators.py:247
msgid "This field must be unique for the \"{date_field}\" year."
msgstr "A mezőnek egyedinek kell lennie a \"{date_field}\" évre."
#: versioning.py:39
msgid "Invalid version in \"Accept\" header."
msgstr "Érvénytelen verzió az \"Accept\" fejlécmezőben."
#: versioning.py:70 versioning.py:112
msgid "Invalid version in URL path."
msgstr "Érvénytelen verzió az URL elérési útban."
#: versioning.py:138
msgid "Invalid version in hostname."
msgstr "Érvénytelen verzió a hosztnévben."
#: versioning.py:160
msgid "Invalid version in query parameter."
msgstr "Érvénytelen verzió a lekérdezési paraméterben."
#: authtoken/serializers.py:20
msgid "User account is disabled."
msgstr "A felhasználó tiltva van."
#: authtoken/serializers.py:23
msgid "Unable to log in with provided credentials."
msgstr "A megadott azonosítókkal nem lehet bejelentkezni."
#: authtoken/serializers.py:26
msgid "Must include \"username\" and \"password\"."
msgstr "Tartalmaznia kell a \"felhasználónevet\" és a \"jelszót\"."

Binary file not shown.

View File

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

Binary file not shown.

View File

@ -0,0 +1,325 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Mattia Procopio <promat85@gmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n"
"Language-Team: Italian (http://www.transifex.com/projects/p/django-rest-framework/language/it/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: it\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: authentication.py:69
msgid "Invalid basic header. No credentials provided."
msgstr ""
#: authentication.py:72
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr ""
#: authentication.py:78
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr ""
#: authentication.py:90
msgid "Invalid username/password."
msgstr "Nome utente/password non validi"
#: authentication.py:156
msgid "Invalid token header. No credentials provided."
msgstr "Header del token non valido. Credenziali non fornite."
#: authentication.py:159
msgid "Invalid token header. Token string should not contain spaces."
msgstr "Header del token non valido. Il contenuto del token non dovrebbe contenere spazi."
#: authentication.py:168
msgid "Invalid token."
msgstr "Token invalido."
#: authentication.py:171
msgid "User inactive or deleted."
msgstr "Utente inattivo o eliminato."
#: exceptions.py:38
msgid "A server error occurred."
msgstr "Errore del server."
#: exceptions.py:73
msgid "Malformed request."
msgstr "Richiesta malformata."
#: exceptions.py:78
msgid "Incorrect authentication credentials."
msgstr "Credenziali di autenticazione incorrette."
#: exceptions.py:83
msgid "Authentication credentials were not provided."
msgstr "Non sono state immesse le credenziali di autenticazione."
#: exceptions.py:88
msgid "You do not have permission to perform this action."
msgstr "Non hai l'autorizzazione per eseguire questa azione."
#: exceptions.py:93
msgid "Not found."
msgstr "Non trovato."
#: exceptions.py:98
msgid "Method \"{method}\" not allowed."
msgstr "Metodo \"{method}\" non consentito"
#: exceptions.py:109
msgid "Could not satisfy the request Accept header."
msgstr "Impossibile soddisfare l'header \"Accept\" presente nella richiesta."
#: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request."
msgstr "Tipo di media \"{media_type}\"non supportato."
#: exceptions.py:134
msgid "Request was throttled."
msgstr ""
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155
msgid "This field is required."
msgstr "Campo obbligatorio."
#: fields.py:154
msgid "This field may not be null."
msgstr "Il campo non puà essere nullo."
#: fields.py:487 fields.py:515
msgid "\"{input}\" is not a valid boolean."
msgstr "\"{input}\" non è un valido valore booleano."
#: fields.py:550
msgid "This field may not be blank."
msgstr "Questo campo non può essere omesso."
#: fields.py:551 fields.py:1324
msgid "Ensure this field has no more than {max_length} characters."
msgstr "Assicurati che questo campo non abbia più di {max_length} caratteri."
#: fields.py:552
msgid "Ensure this field has at least {min_length} characters."
msgstr "Assicurati che questo campo abbia almeno {max_length} caratteri."
#: fields.py:587
msgid "Enter a valid email address."
msgstr "Inserisci un indirizzo email valido."
#: fields.py:604
msgid "This value does not match the required pattern."
msgstr ""
#: fields.py:615
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr "Immetti uno \"slug\" valido che consista di lettere, numeri, underscore o trattini."
#: fields.py:627
msgid "Enter a valid URL."
msgstr "Inserisci un URL valido"
#: fields.py:638
msgid "\"{value}\" is not a valid UUID."
msgstr ""
#: fields.py:657
msgid "A valid integer is required."
msgstr "È richiesto un numero intero valido."
#: fields.py:658 fields.py:692 fields.py:725
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "Assicurati che il valore sia minore o uguale a {max_value}."
#: fields.py:659 fields.py:693 fields.py:726
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "Assicurati che il valore sia maggiore o uguale a {min_value}."
#: fields.py:660 fields.py:694 fields.py:730
msgid "String value too large."
msgstr ""
#: fields.py:691 fields.py:724
msgid "A valid number is required."
msgstr "È richiesto un numero valido."
#: fields.py:727
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "Assicurati che non ci siano più di {max_digits} cifre in totale."
#: fields.py:728
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "Assicurati che non ci siano più di {max_decimal_places} cifre decimali."
#: fields.py:729
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr "Assicurati che non ci siano più di {max_whole_digits} cifre prima del separatore decimale."
#: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "L'oggetto di tipo datetime è in un formato errato. Usa uno dei seguenti formati: {format}."
#: fields.py:814
msgid "Expected a datetime but got a date."
msgstr "Atteso un oggetto di tipo datetime ma l'oggetto ricevuto è di tipo date."
#: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "La data è in un formato errato. Usa uno dei seguenti formati: {format}."
#: fields.py:879
msgid "Expected a date but got a datetime."
msgstr "Atteso un oggetto di tipo date ma l'oggetto ricevuto è di tipo datetime."
#: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr ""
#: fields.py:992 fields.py:1036
msgid "\"{input}\" is not a valid choice."
msgstr "\"{input}\" non è una scelta valida."
#: fields.py:1037 fields.py:1151 serializers.py:482
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "Attesa una lista di oggetti ma l'oggetto ricevuto è di tipo \"{input_type}\"."
#: fields.py:1067
msgid "No file was submitted."
msgstr "Non è stato inviato alcun file."
#: fields.py:1068
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr ""
#: fields.py:1069
msgid "No filename could be determined."
msgstr "Il nome del file non può essere determinato."
#: fields.py:1070
msgid "The submitted file is empty."
msgstr "Il file inviato è vuoto."
#: fields.py:1071
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr ""
#: fields.py:1113
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr ""
#: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr ""
#: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}."
msgstr ""
#: pagination.py:442
msgid "Invalid cursor"
msgstr ""
#: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr ""
#: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr ""
#: relations.py:157
msgid "Invalid hyperlink - No URL match."
msgstr ""
#: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match."
msgstr ""
#: relations.py:159
msgid "Invalid hyperlink - Object does not exist."
msgstr ""
#: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr ""
#: relations.py:295
msgid "Object with {slug_name}={value} does not exist."
msgstr ""
#: relations.py:296
msgid "Invalid value."
msgstr "Valore non valido."
#: serializers.py:299
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr ""
#: validators.py:22
msgid "This field must be unique."
msgstr "Questo campo deve essere unico."
#: validators.py:76
msgid "The fields {field_names} must make a unique set."
msgstr ""
#: validators.py:219
msgid "This field must be unique for the \"{date_field}\" date."
msgstr ""
#: validators.py:234
msgid "This field must be unique for the \"{date_field}\" month."
msgstr ""
#: validators.py:247
msgid "This field must be unique for the \"{date_field}\" year."
msgstr ""
#: versioning.py:39
msgid "Invalid version in \"Accept\" header."
msgstr ""
#: versioning.py:70 versioning.py:112
msgid "Invalid version in URL path."
msgstr ""
#: versioning.py:138
msgid "Invalid version in hostname."
msgstr ""
#: versioning.py:160
msgid "Invalid version in query parameter."
msgstr ""
#: authtoken/serializers.py:20
msgid "User account is disabled."
msgstr "L'account dell'utente è disabilitato"
#: authtoken/serializers.py:23
msgid "Unable to log in with provided credentials."
msgstr "Impossibile eseguire il log in con le credenziali immesse."
#: authtoken/serializers.py:26
msgid "Must include \"username\" and \"password\"."
msgstr "Deve includere \"nome utente\" e \"password\"."

Binary file not shown.

View File

@ -0,0 +1,325 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# SUN CHOI <best2378@gmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n"
"Language-Team: Korean (Korea) (http://www.transifex.com/projects/p/django-rest-framework/language/ko_KR/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ko_KR\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: authentication.py:69
msgid "Invalid basic header. No credentials provided."
msgstr "기본 헤더(basic header)가 유효하지 않습니다. 인증데이터(credentials)가 제공되지 않았습니다."
#: authentication.py:72
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "기본 헤더(basic header)가 유효하지 않습니다. 인증데이터(credentials) 문자열은 빈칸(spaces)을 포함하지 않아야 합니다."
#: authentication.py:78
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "기본 헤더(basic header)가 유효하지 않습니다. 인증데이터(credentials)가 base64로 적절히 부호화(encode)되지 않았습니다."
#: authentication.py:90
msgid "Invalid username/password."
msgstr "아이디/비밀번호가 유효하지 않습니다."
#: authentication.py:156
msgid "Invalid token header. No credentials provided."
msgstr "토큰 헤더가 유효하지 않습니다. 인증데이터(credentials)가 제공되지 않았습니다."
#: authentication.py:159
msgid "Invalid token header. Token string should not contain spaces."
msgstr "토큰 헤더가 유효하지 않습니다. 토큰 문자열은 빈칸(spaces)를 포함하지 않아야 합니다."
#: authentication.py:168
msgid "Invalid token."
msgstr "토큰이 유효하지 않습니다."
#: authentication.py:171
msgid "User inactive or deleted."
msgstr "계정이 중지되었거나 삭제되었습니다."
#: exceptions.py:38
msgid "A server error occurred."
msgstr "서버 장애가 발생했습니다."
#: exceptions.py:73
msgid "Malformed request."
msgstr "잘못된 요청입니다."
#: exceptions.py:78
msgid "Incorrect authentication credentials."
msgstr "자격 인증데이터(authentication credentials)가 정확하지 않습니다."
#: exceptions.py:83
msgid "Authentication credentials were not provided."
msgstr "자격 인증데이터(authentication credentials)가 제공되지 않았습니다."
#: exceptions.py:88
msgid "You do not have permission to perform this action."
msgstr "이 작업을 "
#: exceptions.py:93
msgid "Not found."
msgstr "찾을 수 없습니다."
#: exceptions.py:98
msgid "Method \"{method}\" not allowed."
msgstr "메소드(Method) \"{method}\"는 허용되지 않습니다."
#: exceptions.py:109
msgid "Could not satisfy the request Accept header."
msgstr ""
#: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request."
msgstr "요청된 \"{media_type}\"가 지원되지 않는 미디어 형태입니다."
#: exceptions.py:134
msgid "Request was throttled."
msgstr "요청이 지연(throttled)되었습니다."
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155
msgid "This field is required."
msgstr "이 항목을 채워주십시오."
#: fields.py:154
msgid "This field may not be null."
msgstr ""
#: fields.py:487 fields.py:515
msgid "\"{input}\" is not a valid boolean."
msgstr "\"{input}\"이 유효하지 않은 부울(boolean)입니다."
#: fields.py:550
msgid "This field may not be blank."
msgstr ""
#: fields.py:551 fields.py:1324
msgid "Ensure this field has no more than {max_length} characters."
msgstr "이 칸이 글자 수가 {max_length} 이하인지 확인하십시오."
#: fields.py:552
msgid "Ensure this field has at least {min_length} characters."
msgstr "이 칸이 글자 수가 적어도 {min_length} 이상인지 확인하십시오."
#: fields.py:587
msgid "Enter a valid email address."
msgstr "유효한 이메일 주소를 입력하십시오."
#: fields.py:604
msgid "This value does not match the required pattern."
msgstr "형식에 맞지 않는 값입니다."
#: fields.py:615
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr "문자, 숫자, 밑줄( _ ) 또는 하이픈( - )으로 이루어진 유효한 \"slug\"를 입력하십시오."
#: fields.py:627
msgid "Enter a valid URL."
msgstr "유효한 URL을 입력하십시오."
#: fields.py:638
msgid "\"{value}\" is not a valid UUID."
msgstr ""
#: fields.py:657
msgid "A valid integer is required."
msgstr "유효한 정수(integer)를 넣어주세요."
#: fields.py:658 fields.py:692 fields.py:725
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "이 값이 {max_value}보다 작거나 같은지 확인하십시오."
#: fields.py:659 fields.py:693 fields.py:726
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "이 값이 {min_value}보다 크거나 같은지 확인하십시오."
#: fields.py:660 fields.py:694 fields.py:730
msgid "String value too large."
msgstr "문자열 값이 너무 큽니다."
#: fields.py:691 fields.py:724
msgid "A valid number is required."
msgstr "유효한 숫자를 넣어주세요."
#: fields.py:727
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "전체 숫자(digits)가 {max_digits} 이하인지 확인하십시오."
#: fields.py:728
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "소수점 자릿수가 {max_decimal_places} 이하인지 확인하십시오."
#: fields.py:729
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr "소수점 자리 앞에 숫자(digits)가 {max_whole_digits} 이하인지 확인하십시오."
#: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "Datetime의 포멧이 잘못되었습니다. 이 형식들 중 한가지를 사용하세요: {format}."
#: fields.py:814
msgid "Expected a datetime but got a date."
msgstr "예상된 datatime 대신 date를 받았습니다."
#: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "Date의 포멧이 잘못되었습니다. 이 형식들 중 한가지를 사용하세요: {format}."
#: fields.py:879
msgid "Expected a date but got a datetime."
msgstr "예상된 date 대신 datetime을 받았습니다."
#: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "Time의 포멧이 잘못되었습니다. 이 형식들 중 한가지를 사용하세요: {format}."
#: fields.py:992 fields.py:1036
msgid "\"{input}\" is not a valid choice."
msgstr "\"{input}\"이 유효하지 않은 선택(choice)입니다."
#: fields.py:1037 fields.py:1151 serializers.py:482
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "아이템 리스트가 예상되었으나 \"{input_type}\"를 받았습니다."
#: fields.py:1067
msgid "No file was submitted."
msgstr "파일이 제출되지 않았습니다."
#: fields.py:1068
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr "제출된 데이터는 파일이 아닙니다. 제출된 서식의 인코딩 형식을 확인하세요."
#: fields.py:1069
msgid "No filename could be determined."
msgstr "파일명을 알 수 없습니다."
#: fields.py:1070
msgid "The submitted file is empty."
msgstr "제출된 파일이 비어있습니다."
#: fields.py:1071
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "이 파일명의 글자수가 최대 {max_length}를 넘지 않는지 확인하십시오. (이것은 {length}가 있습니다)."
#: fields.py:1113
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr "유효한 이미지 파일을 업로드 하십시오. 업로드 하신 파일은 이미지 파일이 아니거나 손상된 이미지 파일입니다."
#: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr ""
#: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}."
msgstr "유효하지 않은 page \"{page_number}\": {message}."
#: pagination.py:442
msgid "Invalid cursor"
msgstr ""
#: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "유효하지 않은 pk \"{pk_value}\" - 객체가 존재하지 않습니다."
#: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "잘못된 형식입니다. pk 값 대신 {data_type}를 받았습니다."
#: relations.py:157
msgid "Invalid hyperlink - No URL match."
msgstr "유효하지 않은 하이퍼링크 - 일치하는 URL이 없습니다."
#: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match."
msgstr "유효하지 않은 하이퍼링크 - URL이 일치하지 않습니다."
#: relations.py:159
msgid "Invalid hyperlink - Object does not exist."
msgstr "유효하지 않은 하이퍼링크 - 객체가 존재하지 않습니다."
#: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "잘못된 형식입니다. URL 문자열을 예상했으나 {data_type}을 받았습니다."
#: relations.py:295
msgid "Object with {slug_name}={value} does not exist."
msgstr "{slug_name}={value} 객체가 존재하지 않습니다."
#: relations.py:296
msgid "Invalid value."
msgstr "값이 유효하지 않습니다."
#: serializers.py:299
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "유효하지 않은 데이터. 딕셔너리(dictionary)대신 {datatype}를 받았습니다."
#: validators.py:22
msgid "This field must be unique."
msgstr ""
#: validators.py:76
msgid "The fields {field_names} must make a unique set."
msgstr ""
#: validators.py:219
msgid "This field must be unique for the \"{date_field}\" date."
msgstr ""
#: validators.py:234
msgid "This field must be unique for the \"{date_field}\" month."
msgstr ""
#: validators.py:247
msgid "This field must be unique for the \"{date_field}\" year."
msgstr ""
#: versioning.py:39
msgid "Invalid version in \"Accept\" header."
msgstr ""
#: versioning.py:70 versioning.py:112
msgid "Invalid version in URL path."
msgstr ""
#: versioning.py:138
msgid "Invalid version in hostname."
msgstr ""
#: versioning.py:160
msgid "Invalid version in query parameter."
msgstr ""
#: authtoken/serializers.py:20
msgid "User account is disabled."
msgstr "사용자 계정을 사용할 수 없습니다."
#: authtoken/serializers.py:23
msgid "Unable to log in with provided credentials."
msgstr "제공된 인증데이터(credentials)로는 로그인할 수 없습니다."
#: authtoken/serializers.py:26
msgid "Must include \"username\" and \"password\"."
msgstr "\"아이디\"와 \"비밀번호\"를 포함해야 합니다."

Binary file not shown.

View File

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

Binary file not shown.

View File

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

Binary file not shown.

View File

@ -0,0 +1,326 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Janusz Harkot <jh@blueice.pl>, 2015
# Maciek Olko <maciej.olko@gmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n"
"Language-Team: Polish (http://www.transifex.com/projects/p/django-rest-framework/language/pl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: pl\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#: authentication.py:69
msgid "Invalid basic header. No credentials provided."
msgstr "Niepoprawny podstawowy nagłówek. Brak danych uwierzytelniających."
#: authentication.py:72
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "Niepoprawny podstawowy nagłówek. Ciąg znaków danych uwierzytelniających nie powinien zawierać spacji."
#: authentication.py:78
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "Niepoprawny podstawowy nagłówek. Niewłaściwe kodowanie base64 danych uwierzytelniających."
#: authentication.py:90
msgid "Invalid username/password."
msgstr "Niepoprawna nazwa użytkownika lub hasło."
#: authentication.py:156
msgid "Invalid token header. No credentials provided."
msgstr "Niepoprawny nagłówek tokena. Brak danych uwierzytelniających."
#: authentication.py:159
msgid "Invalid token header. Token string should not contain spaces."
msgstr "Niepoprawny nagłówek tokena. Token nie może zawierać odstępów."
#: authentication.py:168
msgid "Invalid token."
msgstr "Niepoprawny token."
#: authentication.py:171
msgid "User inactive or deleted."
msgstr "Użytkownik nieaktywny lub usunięty."
#: exceptions.py:38
msgid "A server error occurred."
msgstr "Wystąpił błąd serwera."
#: exceptions.py:73
msgid "Malformed request."
msgstr "Zniekształcone żądanie."
#: exceptions.py:78
msgid "Incorrect authentication credentials."
msgstr "Błędne dane uwierzytelniające."
#: exceptions.py:83
msgid "Authentication credentials were not provided."
msgstr "Nie podano danych uwierzytelniających."
#: exceptions.py:88
msgid "You do not have permission to perform this action."
msgstr "Nie masz uprawnień, by wykonać tę czynność."
#: exceptions.py:93
msgid "Not found."
msgstr "Nie znaleziono."
#: exceptions.py:98
msgid "Method \"{method}\" not allowed."
msgstr "Niedozwolona metoda \"{method}\"."
#: exceptions.py:109
msgid "Could not satisfy the request Accept header."
msgstr "Nie można zaspokoić nagłówka Accept żądania."
#: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request."
msgstr "Brak wsparcia dla żądanego typu danych \"{media_type}\"."
#: exceptions.py:134
msgid "Request was throttled."
msgstr "Żądanie zostało zdławione."
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155
msgid "This field is required."
msgstr "To pole jest wymagane."
#: fields.py:154
msgid "This field may not be null."
msgstr "Pole nie może mieć wartości null."
#: fields.py:487 fields.py:515
msgid "\"{input}\" is not a valid boolean."
msgstr "\"{input}\" nie jest poprawną wartością logiczną."
#: fields.py:550
msgid "This field may not be blank."
msgstr "To pole nie może być puste."
#: fields.py:551 fields.py:1324
msgid "Ensure this field has no more than {max_length} characters."
msgstr "Upewnij się, że to pole ma nie więcej niż {max_length} znaków."
#: fields.py:552
msgid "Ensure this field has at least {min_length} characters."
msgstr "Upewnij się, że pole ma co najmniej {min_length} znaków."
#: fields.py:587
msgid "Enter a valid email address."
msgstr "Podaj poprawny adres e-mail."
#: fields.py:604
msgid "This value does not match the required pattern."
msgstr "Ta wartość nie pasuje do wymaganego wzorca."
#: fields.py:615
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr "Wprowadź poprawną wartość pola typu \"slug\", składającą się ze znaków łacińskich, cyfr, podkreślenia lub myślnika."
#: fields.py:627
msgid "Enter a valid URL."
msgstr "Wprowadź poprawny adres URL."
#: fields.py:638
msgid "\"{value}\" is not a valid UUID."
msgstr ""
#: fields.py:657
msgid "A valid integer is required."
msgstr "Wymagana poprawna liczba całkowita."
#: fields.py:658 fields.py:692 fields.py:725
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "Upewnij się, że ta wartość jest mniejsza lub równa {max_value}."
#: fields.py:659 fields.py:693 fields.py:726
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "Upewnij się, że ta wartość jest większa lub równa {min_value}."
#: fields.py:660 fields.py:694 fields.py:730
msgid "String value too large."
msgstr "Za długi ciąg znaków."
#: fields.py:691 fields.py:724
msgid "A valid number is required."
msgstr "Wymagana poprawna liczba."
#: fields.py:727
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "Upewnij się, że liczba ma nie więcej niż {max_digits} cyfr."
#: fields.py:728
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "Upewnij się, że liczba ma nie więcej niż {max_decimal_places} cyfr dziesiętnych."
#: fields.py:729
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr "Upewnij się, że liczba ma nie więcej niż {max_whole_digits} cyfr całkowitych."
#: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "Wartość daty z czasem ma zły format. Użyj jednego z dostępnych formatów: {format}."
#: fields.py:814
msgid "Expected a datetime but got a date."
msgstr "Oczekiwano datę z czasem, otrzymano tylko datę."
#: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "Data ma zły format. Użyj jednego z tych formatów: {format}."
#: fields.py:879
msgid "Expected a date but got a datetime."
msgstr "Oczekiwano daty a otrzymano datę z czasem."
#: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "Błędny format czasu. Użyj jednego z dostępnych formatów: {format}"
#: fields.py:992 fields.py:1036
msgid "\"{input}\" is not a valid choice."
msgstr "\"{input}\" nie jest poprawnym wyborem."
#: fields.py:1037 fields.py:1151 serializers.py:482
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "Oczekiwano listy elementów, a otrzymano dane typu \"{input_type}\"."
#: fields.py:1067
msgid "No file was submitted."
msgstr "Nie przesłano pliku."
#: fields.py:1068
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr "Przesłane dane nie były plikiem. Sprawdź typ kodowania formatki."
#: fields.py:1069
msgid "No filename could be determined."
msgstr "Nie można określić nazwy pliku."
#: fields.py:1070
msgid "The submitted file is empty."
msgstr "Przesłany plik jest pusty."
#: fields.py:1071
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "Upewnij się, że nazwa pliku ma długość co najwyżej {max_length} znaków (aktualnie ma {length})."
#: fields.py:1113
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr "Prześlij poprawny plik graficzny. Przesłany plik albo nie jest grafiką lub jest uszkodzony."
#: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr ""
#: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}."
msgstr "Niepoprawna strona \"{page_number}\": {message}."
#: pagination.py:442
msgid "Invalid cursor"
msgstr ""
#: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "Błędny klucz główny \"{pk_value}\" - obiekt nie istnieje."
#: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "Błędny typ danych. Oczekiwano wartość klucza głównego, otrzymano {data_type}."
#: relations.py:157
msgid "Invalid hyperlink - No URL match."
msgstr "Błędny hyperlink - nie znaleziono pasującego adresu URL."
#: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match."
msgstr "Błędny hyperlink - błędne dopasowanie adresu URL."
#: relations.py:159
msgid "Invalid hyperlink - Object does not exist."
msgstr "Błędny hyperlink - obiekt nie istnieje."
#: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "Błędny typ danych. Oczekiwano adresu URL, otrzymano {data_type}"
#: relations.py:295
msgid "Object with {slug_name}={value} does not exist."
msgstr "Obiekt z polem {slug_name}={value} nie istnieje"
#: relations.py:296
msgid "Invalid value."
msgstr "Niepoprawna wartość."
#: serializers.py:299
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "Niepoprawne dane. Oczekiwano słownika, otrzymano {datatype}."
#: validators.py:22
msgid "This field must be unique."
msgstr "Wartość dla tego pola musi być unikalna."
#: validators.py:76
msgid "The fields {field_names} must make a unique set."
msgstr "Pola {field_names} muszą tworzyć unikalny zestaw."
#: validators.py:219
msgid "This field must be unique for the \"{date_field}\" date."
msgstr "To pole musi mieć unikalną wartość dla jednej daty z pola \"{date_field}\"."
#: validators.py:234
msgid "This field must be unique for the \"{date_field}\" month."
msgstr "To pole musi mieć unikalną wartość dla konkretnego miesiąca z pola \"{date_field}\"."
#: validators.py:247
msgid "This field must be unique for the \"{date_field}\" year."
msgstr "To pole musi mieć unikalną wartość dla konkretnego roku z pola \"{date_field}\"."
#: versioning.py:39
msgid "Invalid version in \"Accept\" header."
msgstr "Błędna wersja w nagłówku \"Accept\"."
#: versioning.py:70 versioning.py:112
msgid "Invalid version in URL path."
msgstr "Błędna wersja w ścieżce URL."
#: versioning.py:138
msgid "Invalid version in hostname."
msgstr "Błędna wersja w nazwie hosta."
#: versioning.py:160
msgid "Invalid version in query parameter."
msgstr "Błędna wersja w parametrach zapytania."
#: authtoken/serializers.py:20
msgid "User account is disabled."
msgstr "Konto użytkownika jest nieaktywne."
#: authtoken/serializers.py:23
msgid "Unable to log in with provided credentials."
msgstr "Podane dane uwierzytelniające nie pozwalają na zalogowanie."
#: authtoken/serializers.py:26
msgid "Must include \"username\" and \"password\"."
msgstr "Musi zawierać \"username\" i \"password\"."

Binary file not shown.

View File

@ -0,0 +1,326 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Craig Blaszczyk <masterjakul@gmail.com>, 2015
# Filipe Rinaldi <filipe.rinaldi@gmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n"
"Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/django-rest-framework/language/pt_BR/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: pt_BR\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: authentication.py:69
msgid "Invalid basic header. No credentials provided."
msgstr "Cabeçalho básico inválido. Credenciais não fornecidas."
#: authentication.py:72
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "Cabeçalho básico inválido. String de credenciais não deve incluir espaços."
#: authentication.py:78
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "Cabeçalho básico inválido. Credenciais codificadas em base64 incorretamente."
#: authentication.py:90
msgid "Invalid username/password."
msgstr "Usário ou senha inválido."
#: authentication.py:156
msgid "Invalid token header. No credentials provided."
msgstr "Cabeçalho de token inválido. Credenciais não fornecidas."
#: authentication.py:159
msgid "Invalid token header. Token string should not contain spaces."
msgstr "Cabeçalho de token inválido. String de token não deve incluir espaços."
#: authentication.py:168
msgid "Invalid token."
msgstr "Token inválido."
#: authentication.py:171
msgid "User inactive or deleted."
msgstr "Usuário inativo ou removido."
#: exceptions.py:38
msgid "A server error occurred."
msgstr "Ocorreu um erro de servidor."
#: exceptions.py:73
msgid "Malformed request."
msgstr "Pedido malformado."
#: exceptions.py:78
msgid "Incorrect authentication credentials."
msgstr "Credenciais de autenticação incorretas."
#: exceptions.py:83
msgid "Authentication credentials were not provided."
msgstr "As credenciais de autenticação não foram fornecidas."
#: exceptions.py:88
msgid "You do not have permission to perform this action."
msgstr "Você não tem persmissao para executar essa ação."
#: exceptions.py:93
msgid "Not found."
msgstr "Não encontrado."
#: exceptions.py:98
msgid "Method \"{method}\" not allowed."
msgstr "Método \"{method}\" não é permitido."
#: exceptions.py:109
msgid "Could not satisfy the request Accept header."
msgstr "Não foi possível satisfazer a requisição do cabeçalho Accept."
#: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request."
msgstr "Media type \"{media_type}\" no pedido não é suportado."
#: exceptions.py:134
msgid "Request was throttled."
msgstr "Pedido foi limitado."
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155
msgid "This field is required."
msgstr "Este campo é obrigatório."
#: fields.py:154
msgid "This field may not be null."
msgstr "Este campo não pode ser nulo."
#: fields.py:487 fields.py:515
msgid "\"{input}\" is not a valid boolean."
msgstr "\"{input}\" não é um valor boleano válido."
#: fields.py:550
msgid "This field may not be blank."
msgstr "Este campo não pode ser em branco."
#: fields.py:551 fields.py:1324
msgid "Ensure this field has no more than {max_length} characters."
msgstr "Certifique-se de que este campo não tenha mais de {max_length} caracteres."
#: fields.py:552
msgid "Ensure this field has at least {min_length} characters."
msgstr "Certifique-se de que este campo tenha mais de {min_length} caracteres."
#: fields.py:587
msgid "Enter a valid email address."
msgstr "Insira um endereço de email válido."
#: fields.py:604
msgid "This value does not match the required pattern."
msgstr "Este valor não corresponde ao padrão exigido."
#: fields.py:615
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr "Entrar um \"slug\" válido que consista de letras, números, sublinhados ou hífens."
#: fields.py:627
msgid "Enter a valid URL."
msgstr "Entrar um URL válido."
#: fields.py:638
msgid "\"{value}\" is not a valid UUID."
msgstr ""
#: fields.py:657
msgid "A valid integer is required."
msgstr "Um número inteiro válido é exigido."
#: fields.py:658 fields.py:692 fields.py:725
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "Certifique-se de que este valor seja inferior ou igual a {max_value}."
#: fields.py:659 fields.py:693 fields.py:726
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "Certifque-se de que este valor seja maior ou igual a {min_value}."
#: fields.py:660 fields.py:694 fields.py:730
msgid "String value too large."
msgstr "Valor da string é muito grande."
#: fields.py:691 fields.py:724
msgid "A valid number is required."
msgstr "Um número válido é necessário."
#: fields.py:727
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "Certifique-se de que não haja mais de {max_digits} dígitos no total."
#: fields.py:728
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "Certifique-se de que não haja mais de {max_decimal_places} casas decimais."
#: fields.py:729
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr "Certifique-se de que não haja mais de {max_whole_digits} dígitos antes do ponto decimal."
#: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "Formato inválido para data e hora. Use um dos formatos a seguir: {format}."
#: fields.py:814
msgid "Expected a datetime but got a date."
msgstr "Data e hora são necessários mas apenas data foi encontrada."
#: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "Formato inválido para data. Use um dos formatos a seguir: {format}."
#: fields.py:879
msgid "Expected a date but got a datetime."
msgstr "Necessário uma data mas recebeu uma data e hora."
#: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "Tempo tem formato errado. Usa um desses em vez disso: {format}."
#: fields.py:992 fields.py:1036
msgid "\"{input}\" is not a valid choice."
msgstr "\"{input}\" não é um escolha válido."
#: fields.py:1037 fields.py:1151 serializers.py:482
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "Necessário uma lista de itens, mas recebeu tipo \"{input_type}\"."
#: fields.py:1067
msgid "No file was submitted."
msgstr "Ficheiro não foi submetido."
#: fields.py:1068
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr "Os dados submetidos nao foram um ficheiro. Certifique-se do tipo de codificação no formulário."
#: fields.py:1069
msgid "No filename could be determined."
msgstr "Nome do arquivo não pode ser determinado."
#: fields.py:1070
msgid "The submitted file is empty."
msgstr "O arquivo submetido ésta vázio."
#: fields.py:1071
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "Certifique-se de que o nome do ficheiro tem menos de {max_length} caracteres (tem {length})."
#: fields.py:1113
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr "Fazer upload de um imagem válido. O arquivo mandou não foi um imagem ou foi corrupto."
#: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr ""
#: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}."
msgstr "Página inválido \"{page_number}\": {message}."
#: pagination.py:442
msgid "Invalid cursor"
msgstr ""
#: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "Pk inválido \"{pk_value}\" - objeto não existe."
#: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "Tipo incorreto. Necessário valor pk, recebeu {data_type}."
#: relations.py:157
msgid "Invalid hyperlink - No URL match."
msgstr "Hyperlink inválido - URL não combinou."
#: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match."
msgstr "Hyperlink inválido - URL combinou errado."
#: relations.py:159
msgid "Invalid hyperlink - Object does not exist."
msgstr "Hyperlink inválido - objeto não existe."
#: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "Tipo incorreto. Necessário string URL, recebeu {data_type}."
#: relations.py:295
msgid "Object with {slug_name}={value} does not exist."
msgstr "Objeto com {slug_name}={value} não existe."
#: relations.py:296
msgid "Invalid value."
msgstr "Valor inválido."
#: serializers.py:299
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "Data inválido. Necessário um dicionário mas recebeu {datatype}."
#: validators.py:22
msgid "This field must be unique."
msgstr "Esse campo deve ser unico."
#: validators.py:76
msgid "The fields {field_names} must make a unique set."
msgstr "Os campos {field_names} devem criar um set unico."
#: validators.py:219
msgid "This field must be unique for the \"{date_field}\" date."
msgstr "O campo deve ser unico pela data \"{date_field}\"."
#: validators.py:234
msgid "This field must be unique for the \"{date_field}\" month."
msgstr "O campo deve ser unico pelo anô \"{date_field}\"."
#: validators.py:247
msgid "This field must be unique for the \"{date_field}\" year."
msgstr "O campo deve ser unico pela mês \"{date_field}\"."
#: versioning.py:39
msgid "Invalid version in \"Accept\" header."
msgstr "Versão inválido no cabeçalho \"Accept\"."
#: versioning.py:70 versioning.py:112
msgid "Invalid version in URL path."
msgstr "Versão inválido no caminho de URL."
#: versioning.py:138
msgid "Invalid version in hostname."
msgstr "Versão inválido no hostname."
#: versioning.py:160
msgid "Invalid version in query parameter."
msgstr "Versão inválida no parâmetro de query."
#: authtoken/serializers.py:20
msgid "User account is disabled."
msgstr "Conta de usário desabilitada."
#: authtoken/serializers.py:23
msgid "Unable to log in with provided credentials."
msgstr "Impossível fazer login com as credenciais fornecidas."
#: authtoken/serializers.py:26
msgid "Must include \"username\" and \"password\"."
msgstr "Obrigatório incluir \"usuário\" e \"senha\"."

Binary file not shown.

View File

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

Binary file not shown.

View File

@ -0,0 +1,325 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Stanislav Komanec <stano@videoflot.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n"
"Language-Team: Slovak (http://www.transifex.com/projects/p/django-rest-framework/language/sk/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: sk\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: authentication.py:69
msgid "Invalid basic header. No credentials provided."
msgstr "Nesprávna hlavička. Neboli poskytnuté prihlasovacie údaje."
#: authentication.py:72
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "Nesprávna hlavička. Prihlasovacie údaje nesmú obsahovať medzery."
#: authentication.py:78
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "Nesprávna hlavička. Prihlasovacie údaje nie sú správne zakódované pomocou metódy base64."
#: authentication.py:90
msgid "Invalid username/password."
msgstr "Nesprávne prihlasovacie údaje."
#: authentication.py:156
msgid "Invalid token header. No credentials provided."
msgstr "Nesprávna token hlavička. Neboli poskytnuté prihlasovacie údaje."
#: authentication.py:159
msgid "Invalid token header. Token string should not contain spaces."
msgstr "Nesprávna token hlavička. Token hlavička nesmie obsahovať medzery."
#: authentication.py:168
msgid "Invalid token."
msgstr "Nesprávny token."
#: authentication.py:171
msgid "User inactive or deleted."
msgstr "Daný používateľ je neaktívny, alebo zmazaný."
#: exceptions.py:38
msgid "A server error occurred."
msgstr "Vyskytla sa chyba na strane servera."
#: exceptions.py:73
msgid "Malformed request."
msgstr "Požiadavok má nesprávny formát, alebo je poškodený."
#: exceptions.py:78
msgid "Incorrect authentication credentials."
msgstr "Nesprávne prihlasovacie údaje."
#: exceptions.py:83
msgid "Authentication credentials were not provided."
msgstr "Prihlasovacie údaje neboli zadané."
#: exceptions.py:88
msgid "You do not have permission to perform this action."
msgstr "K danej akcii nemáte oprávnenie."
#: exceptions.py:93
msgid "Not found."
msgstr "Nebolo nájdené."
#: exceptions.py:98
msgid "Method \"{method}\" not allowed."
msgstr "Metóda \"{method}\" nie je povolená."
#: exceptions.py:109
msgid "Could not satisfy the request Accept header."
msgstr "Nie je možné vyhovieť požiadavku v hlavičke \"Accept\"."
#: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request."
msgstr "Požiadavok obsahuje nepodporovaný media type: \"{media_type}\"."
#: exceptions.py:134
msgid "Request was throttled."
msgstr ""
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155
msgid "This field is required."
msgstr ""
#: fields.py:154
msgid "This field may not be null."
msgstr ""
#: fields.py:487 fields.py:515
msgid "\"{input}\" is not a valid boolean."
msgstr ""
#: fields.py:550
msgid "This field may not be blank."
msgstr ""
#: fields.py:551 fields.py:1324
msgid "Ensure this field has no more than {max_length} characters."
msgstr ""
#: fields.py:552
msgid "Ensure this field has at least {min_length} characters."
msgstr ""
#: fields.py:587
msgid "Enter a valid email address."
msgstr ""
#: fields.py:604
msgid "This value does not match the required pattern."
msgstr ""
#: fields.py:615
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr ""
#: fields.py:627
msgid "Enter a valid URL."
msgstr ""
#: fields.py:638
msgid "\"{value}\" is not a valid UUID."
msgstr ""
#: fields.py:657
msgid "A valid integer is required."
msgstr ""
#: fields.py:658 fields.py:692 fields.py:725
msgid "Ensure this value is less than or equal to {max_value}."
msgstr ""
#: fields.py:659 fields.py:693 fields.py:726
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr ""
#: fields.py:660 fields.py:694 fields.py:730
msgid "String value too large."
msgstr ""
#: fields.py:691 fields.py:724
msgid "A valid number is required."
msgstr ""
#: fields.py:727
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr ""
#: fields.py:728
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr ""
#: fields.py:729
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr ""
#: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr ""
#: fields.py:814
msgid "Expected a datetime but got a date."
msgstr ""
#: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr ""
#: fields.py:879
msgid "Expected a date but got a datetime."
msgstr ""
#: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr ""
#: fields.py:992 fields.py:1036
msgid "\"{input}\" is not a valid choice."
msgstr ""
#: fields.py:1037 fields.py:1151 serializers.py:482
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr ""
#: fields.py:1067
msgid "No file was submitted."
msgstr ""
#: fields.py:1068
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr ""
#: fields.py:1069
msgid "No filename could be determined."
msgstr ""
#: fields.py:1070
msgid "The submitted file is empty."
msgstr ""
#: fields.py:1071
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr ""
#: fields.py:1113
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr ""
#: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr ""
#: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}."
msgstr ""
#: pagination.py:442
msgid "Invalid cursor"
msgstr ""
#: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr ""
#: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr ""
#: relations.py:157
msgid "Invalid hyperlink - No URL match."
msgstr ""
#: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match."
msgstr ""
#: relations.py:159
msgid "Invalid hyperlink - Object does not exist."
msgstr ""
#: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr ""
#: relations.py:295
msgid "Object with {slug_name}={value} does not exist."
msgstr ""
#: relations.py:296
msgid "Invalid value."
msgstr ""
#: serializers.py:299
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr ""
#: validators.py:22
msgid "This field must be unique."
msgstr ""
#: validators.py:76
msgid "The fields {field_names} must make a unique set."
msgstr ""
#: validators.py:219
msgid "This field must be unique for the \"{date_field}\" date."
msgstr ""
#: validators.py:234
msgid "This field must be unique for the \"{date_field}\" month."
msgstr ""
#: validators.py:247
msgid "This field must be unique for the \"{date_field}\" year."
msgstr ""
#: versioning.py:39
msgid "Invalid version in \"Accept\" header."
msgstr ""
#: versioning.py:70 versioning.py:112
msgid "Invalid version in URL path."
msgstr ""
#: versioning.py:138
msgid "Invalid version in hostname."
msgstr ""
#: versioning.py:160
msgid "Invalid version in query parameter."
msgstr ""
#: authtoken/serializers.py:20
msgid "User account is disabled."
msgstr "Daný používateľ je zablokovaný."
#: authtoken/serializers.py:23
msgid "Unable to log in with provided credentials."
msgstr "S danými prihlasovacími údajmi nebolo možné sa prihlásiť."
#: authtoken/serializers.py:26
msgid "Must include \"username\" and \"password\"."
msgstr "Musí obsahovať parametre \"používateľské meno\" a \"heslo\"."

Binary file not shown.

View File

@ -0,0 +1,325 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Joakim Soderlund, 2015
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n"
"Language-Team: Swedish (http://www.transifex.com/projects/p/django-rest-framework/language/sv/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: sv\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: authentication.py:69
msgid "Invalid basic header. No credentials provided."
msgstr "Ogiltig \"basic\"-header. Inga användaruppgifter tillhandahölls."
#: authentication.py:72
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "Ogiltig \"basic\"-header. Strängen för användaruppgifterna ska inte innehålla mellanslag."
#: authentication.py:78
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "Ogiltig \"basic\"-header. Användaruppgifterna är inte korrekt base64-kodade."
#: authentication.py:90
msgid "Invalid username/password."
msgstr "Ogiltigt användarnamn/lösenord."
#: authentication.py:156
msgid "Invalid token header. No credentials provided."
msgstr "Ogiltig \"token\"-header. Inga användaruppgifter tillhandahölls."
#: authentication.py:159
msgid "Invalid token header. Token string should not contain spaces."
msgstr "Ogiltig \"token\"-header. Strängen för referensen ska inte innehålla mellanslag."
#: authentication.py:168
msgid "Invalid token."
msgstr "Ogiltig \"token\"."
#: authentication.py:171
msgid "User inactive or deleted."
msgstr "Användaren borttagen eller inaktiv."
#: exceptions.py:38
msgid "A server error occurred."
msgstr "Ett serverfel inträffade."
#: exceptions.py:73
msgid "Malformed request."
msgstr "Ogiltig förfrågan."
#: exceptions.py:78
msgid "Incorrect authentication credentials."
msgstr "Ogiltiga inloggningsuppgifter. "
#: exceptions.py:83
msgid "Authentication credentials were not provided."
msgstr "Autentiseringsuppgifter ej tillhandahållna."
#: exceptions.py:88
msgid "You do not have permission to perform this action."
msgstr "Du har inte tillåtelse att utföra denna förfrågan."
#: exceptions.py:93
msgid "Not found."
msgstr "Hittades inte."
#: exceptions.py:98
msgid "Method \"{method}\" not allowed."
msgstr "Metoden \"{method}\" tillåts inte."
#: exceptions.py:109
msgid "Could not satisfy the request Accept header."
msgstr "Kunde inte tillfredsställa förfrågans \"Accept\"-header."
#: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request."
msgstr "Medietypen \"{media_type}\" stöds inte."
#: exceptions.py:134
msgid "Request was throttled."
msgstr "Förfrågan stoppades eftersom du har skickat för många."
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155
msgid "This field is required."
msgstr "Det här fältet är obligatoriskt."
#: fields.py:154
msgid "This field may not be null."
msgstr "Det här fältet får inte vara null."
#: fields.py:487 fields.py:515
msgid "\"{input}\" is not a valid boolean."
msgstr "\"{input}\" är inte ett giltigt booleskt värde."
#: fields.py:550
msgid "This field may not be blank."
msgstr "Det här fältet får inte vara blankt."
#: fields.py:551 fields.py:1324
msgid "Ensure this field has no more than {max_length} characters."
msgstr "Se till att detta fält inte har fler än {max_length} tecken."
#: fields.py:552
msgid "Ensure this field has at least {min_length} characters."
msgstr "Se till att detta fält har minst {min_length} tecken."
#: fields.py:587
msgid "Enter a valid email address."
msgstr "Ange en giltig mejladress."
#: fields.py:604
msgid "This value does not match the required pattern."
msgstr "Det här värdet matchar inte mallen."
#: fields.py:615
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr "Ange en giltig \"slug\" bestående av bokstäver, nummer, understreck eller bindestreck."
#: fields.py:627
msgid "Enter a valid URL."
msgstr "Ange en giltig URL."
#: fields.py:638
msgid "\"{value}\" is not a valid UUID."
msgstr ""
#: fields.py:657
msgid "A valid integer is required."
msgstr "Ett giltigt heltal krävs."
#: fields.py:658 fields.py:692 fields.py:725
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "Se till att detta värde är mindre än eller lika med {max_value}."
#: fields.py:659 fields.py:693 fields.py:726
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "Se till att detta värde är större än eller lika med {min_value}."
#: fields.py:660 fields.py:694 fields.py:730
msgid "String value too large."
msgstr "Textvärdet är för långt."
#: fields.py:691 fields.py:724
msgid "A valid number is required."
msgstr "Ett giltigt nummer krävs."
#: fields.py:727
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "Se till att det inte finns fler än totalt {max_digits} siffror."
#: fields.py:728
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "Se till att det inte finns fler än {max_decimal_places} decimaler."
#: fields.py:729
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr "Se till att det inte finns fler än {max_whole_digits} siffror före decimalpunkten."
#: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "Datumtiden har fel format. Använd ett av dessa format istället: {format}."
#: fields.py:814
msgid "Expected a datetime but got a date."
msgstr "Förväntade en datumtid men fick ett datum."
#: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "Datumet har fel format. Använde ett av dessa format istället: {format}."
#: fields.py:879
msgid "Expected a date but got a datetime."
msgstr "Förväntade ett datum men fick en datumtid."
#: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "Tiden har fel format. Använd ett av dessa format istället: {format}."
#: fields.py:992 fields.py:1036
msgid "\"{input}\" is not a valid choice."
msgstr "\"{input}\" är inte ett giltigt val."
#: fields.py:1037 fields.py:1151 serializers.py:482
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "Förväntade en lista med element men fick typen \"{input_type}\"."
#: fields.py:1067
msgid "No file was submitted."
msgstr "Ingen fil skickades."
#: fields.py:1068
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr "Den skickade informationen var inte en fil. Kontrollera formulärets kodningstyp."
#: fields.py:1069
msgid "No filename could be determined."
msgstr "Inget filnamn kunde bestämmas."
#: fields.py:1070
msgid "The submitted file is empty."
msgstr "Den skickade filen var tom."
#: fields.py:1071
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "Se till att det här filnamnet har högst {max_length} tecken (det har {length})."
#: fields.py:1113
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr "Ladda upp en giltig bild. Filen du laddade upp var antingen inte en bild eller en skadad bild."
#: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr ""
#: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}."
msgstr "Ogiltigt sida \"{page_number}\": {message}."
#: pagination.py:442
msgid "Invalid cursor"
msgstr ""
#: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "Ogiltigt pk \"{pk_value}\" - Objektet finns inte."
#: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "Felaktig typ. Förväntade pk-värde, fick {data_type}."
#: relations.py:157
msgid "Invalid hyperlink - No URL match."
msgstr "Ogiltig hyperlänk - Ingen URL matchade."
#: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match."
msgstr "Ogiltig hyperlänk - Felaktig URL-matching."
#: relations.py:159
msgid "Invalid hyperlink - Object does not exist."
msgstr "Ogiltig hyperlänk - Objektet finns inte."
#: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "Felaktig typ. Förväntade URL-sträng, fick {data_type}."
#: relations.py:295
msgid "Object with {slug_name}={value} does not exist."
msgstr "Objekt med {slug_name}={value} finns inte."
#: relations.py:296
msgid "Invalid value."
msgstr "Ogiltigt värde."
#: serializers.py:299
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "Ogiltig data. Förväntade en dictionary, men fick {datatype}."
#: validators.py:22
msgid "This field must be unique."
msgstr "Det här fältet måste vara unikt."
#: validators.py:76
msgid "The fields {field_names} must make a unique set."
msgstr "Fälten {field_names} måste skapa ett unikt set."
#: validators.py:219
msgid "This field must be unique for the \"{date_field}\" date."
msgstr "Det här fältet måste vara unikt för datumet \"{date_field}\"."
#: validators.py:234
msgid "This field must be unique for the \"{date_field}\" month."
msgstr "Det här fältet måste vara unikt för månaden \"{date_field}\"."
#: validators.py:247
msgid "This field must be unique for the \"{date_field}\" year."
msgstr "Det här fältet måste vara unikt för året \"{date_field}\"."
#: versioning.py:39
msgid "Invalid version in \"Accept\" header."
msgstr "Ogiltig version i \"Accept\"-headern."
#: versioning.py:70 versioning.py:112
msgid "Invalid version in URL path."
msgstr "Ogiltig version i URL-resursen."
#: versioning.py:138
msgid "Invalid version in hostname."
msgstr "Ogiltig version i värdnamnet."
#: versioning.py:160
msgid "Invalid version in query parameter."
msgstr "Ogiltig version i förfrågningsparametern."
#: authtoken/serializers.py:20
msgid "User account is disabled."
msgstr "Användarkontot är borttaget."
#: authtoken/serializers.py:23
msgid "Unable to log in with provided credentials."
msgstr "Kunde inte logga in med de angivna inloggningsuppgifterna."
#: authtoken/serializers.py:26
msgid "Must include \"username\" and \"password\"."
msgstr "Användarnamn och lösenord måste anges."

Binary file not shown.

View File

@ -0,0 +1,328 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Ertaç Paprat <epaprat@gmail.com>, 2015
# Mesut Can Gürle <mesutcang@gmail.com>, 2015
# Recep KIRMIZI <rkirmizi@gmail.com>, 2015
# Ülgen Sarıkavak <ulgensrkvk@gmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n"
"Language-Team: Turkish (http://www.transifex.com/projects/p/django-rest-framework/language/tr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: tr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: authentication.py:69
msgid "Invalid basic header. No credentials provided."
msgstr ""
#: authentication.py:72
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr ""
#: authentication.py:78
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr ""
#: authentication.py:90
msgid "Invalid username/password."
msgstr "Geçersiz kullanıcı adı/parola"
#: authentication.py:156
msgid "Invalid token header. No credentials provided."
msgstr "Geçersiz token başlığı. Kimlik bilgileri eksik."
#: authentication.py:159
msgid "Invalid token header. Token string should not contain spaces."
msgstr "Geçersiz token başlığı. Token'da boşluk olmamalı."
#: authentication.py:168
msgid "Invalid token."
msgstr "Geçersiz token."
#: authentication.py:171
msgid "User inactive or deleted."
msgstr "Kullanıcı aktif değil ya da silinmiş."
#: exceptions.py:38
msgid "A server error occurred."
msgstr "Sunucu hatası oluştu."
#: exceptions.py:73
msgid "Malformed request."
msgstr "Bozuk istek."
#: exceptions.py:78
msgid "Incorrect authentication credentials."
msgstr "Giriş bilgileri hatalı."
#: exceptions.py:83
msgid "Authentication credentials were not provided."
msgstr "Giriş bilgileri verilmedi."
#: exceptions.py:88
msgid "You do not have permission to perform this action."
msgstr "Bu işlemi yapmak için izniniz bulunmuyor."
#: exceptions.py:93
msgid "Not found."
msgstr "Bulunamadı."
#: exceptions.py:98
msgid "Method \"{method}\" not allowed."
msgstr "\"{method}\" metoduna izin verilmiyor."
#: exceptions.py:109
msgid "Could not satisfy the request Accept header."
msgstr ""
#: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request."
msgstr "İstekte desteklenmeyen medya tipi: \"{media_type}\"."
#: exceptions.py:134
msgid "Request was throttled."
msgstr ""
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155
msgid "This field is required."
msgstr "Bu alan zorunlu."
#: fields.py:154
msgid "This field may not be null."
msgstr "Bu alan boş bırakılmamalı."
#: fields.py:487 fields.py:515
msgid "\"{input}\" is not a valid boolean."
msgstr "\"{input}\" geçerli bir boolean değil."
#: fields.py:550
msgid "This field may not be blank."
msgstr "Bu alan boş bırakılmamalı."
#: fields.py:551 fields.py:1324
msgid "Ensure this field has no more than {max_length} characters."
msgstr "Bu alanın {max_length} karakterden fazla karakter barındırmadığından emin olun."
#: fields.py:552
msgid "Ensure this field has at least {min_length} characters."
msgstr "Bu alanın en az {min_length} karakter barındırdığından emin olun."
#: fields.py:587
msgid "Enter a valid email address."
msgstr "Geçerli bir e-posta adresi girin."
#: fields.py:604
msgid "This value does not match the required pattern."
msgstr "Bu değer gereken düzenli ifade deseni ile uyuşmuyor."
#: fields.py:615
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr "Harf, rakam, altçizgi veya tireden oluşan geçerli bir \"slug\" giriniz."
#: fields.py:627
msgid "Enter a valid URL."
msgstr "Geçerli bir URL girin."
#: fields.py:638
msgid "\"{value}\" is not a valid UUID."
msgstr ""
#: fields.py:657
msgid "A valid integer is required."
msgstr "Geçerli bir tam sayı girin."
#: fields.py:658 fields.py:692 fields.py:725
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "Değerin {max_value} değerinden küçük ya da eşit olduğundan emin olun."
#: fields.py:659 fields.py:693 fields.py:726
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "Değerin {min_value} değerinden büyük ya da eşit olduğundan emin olun."
#: fields.py:660 fields.py:694 fields.py:730
msgid "String value too large."
msgstr "String değeri çok uzun."
#: fields.py:691 fields.py:724
msgid "A valid number is required."
msgstr "Geçerli bir numara gerekiyor."
#: fields.py:727
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "Toplamda {max_digits} haneden fazla hane olmadığından emin olun."
#: fields.py:728
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "Ondalık basamak değerinin {max_decimal_places} haneden fazla olmadığından emin olun."
#: fields.py:729
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr "Ondalık ayracından önce {max_whole_digits} basamaktan fazla olmadığından emin olun."
#: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "Datetime alanı yanlış biçimde. {format} biçimlerinden birini kullanın."
#: fields.py:814
msgid "Expected a datetime but got a date."
msgstr "Datetime değeri bekleniyor, ama date değeri geldi."
#: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "Tarih biçimi yanlış. {format} biçimlerinden birini kullanın."
#: fields.py:879
msgid "Expected a date but got a datetime."
msgstr "Date tipi beklenmekteydi, fakat datetime tipi geldi."
#: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "Time biçimi yanlış. {format} biçimlerinden birini kullanın."
#: fields.py:992 fields.py:1036
msgid "\"{input}\" is not a valid choice."
msgstr "\"{input}\" geçerli bir seçim değil."
#: fields.py:1037 fields.py:1151 serializers.py:482
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "Elemanların listesi beklenirken \"{input_type}\" alındı."
#: fields.py:1067
msgid "No file was submitted."
msgstr "Hiçbir dosya verilmedi."
#: fields.py:1068
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr "Gönderilen veri dosya değil. Formdaki kodlama tipini kontrol edin."
#: fields.py:1069
msgid "No filename could be determined."
msgstr "Hiçbir dosya adı belirlenemedi."
#: fields.py:1070
msgid "The submitted file is empty."
msgstr "Gönderilen dosya boş."
#: fields.py:1071
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "Bu dosya adının en fazla {max_length} karakter uzunluğunda olduğundan emin olun. (şu anda {length} karakter)."
#: fields.py:1113
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr "Geçerli bir resim yükleyin. Yüklediğiniz dosya resim değil ya da bozuk."
#: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr ""
#: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}."
msgstr "Geçersiz sayfa \"{page_number}\":{message}."
#: pagination.py:442
msgid "Invalid cursor"
msgstr ""
#: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "Geçersiz pk \"{pk_value}\" - obje bulunamadı."
#: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "Hatalı tip. Pk değeri beklenirken, alınan {data_type}."
#: relations.py:157
msgid "Invalid hyperlink - No URL match."
msgstr "Geçersiz bağlantı - Hiçbir URL eşleşmedi."
#: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match."
msgstr "Geçersiz bağlantı - Yanlış URL eşleşmesi."
#: relations.py:159
msgid "Invalid hyperlink - Object does not exist."
msgstr "Geçersiz bağlantı - Obje bulunamadı."
#: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "Hatalı tip. URL metni bekleniyor, {data_type} alındı."
#: relations.py:295
msgid "Object with {slug_name}={value} does not exist."
msgstr "{slug_name}={value} değerini taşıyan obje bulunamadı."
#: relations.py:296
msgid "Invalid value."
msgstr "Geçersiz değer."
#: serializers.py:299
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "Geçersiz veri. Sözlük bekleniyordu fakat {datatype} geldi. "
#: validators.py:22
msgid "This field must be unique."
msgstr "Bu alan eşsiz olmalı."
#: validators.py:76
msgid "The fields {field_names} must make a unique set."
msgstr "{field_names} hep birlikte eşsiz bir küme oluşturmalılar."
#: validators.py:219
msgid "This field must be unique for the \"{date_field}\" date."
msgstr "Bu alan \"{date_field}\" tarihine göre eşsiz olmalı."
#: validators.py:234
msgid "This field must be unique for the \"{date_field}\" month."
msgstr "Bu alan \"{date_field}\" ayına göre eşsiz olmalı."
#: validators.py:247
msgid "This field must be unique for the \"{date_field}\" year."
msgstr "Bu alan \"{date_field}\" yılına göre eşsiz olmalı."
#: versioning.py:39
msgid "Invalid version in \"Accept\" header."
msgstr "\"Accept\" başlığındaki sürüm geçersiz."
#: versioning.py:70 versioning.py:112
msgid "Invalid version in URL path."
msgstr "URL dizininde geçersiz versiyon."
#: versioning.py:138
msgid "Invalid version in hostname."
msgstr "Host adında geçersiz versiyon."
#: versioning.py:160
msgid "Invalid version in query parameter."
msgstr "Sorgu parametresinde geçersiz versiyon."
#: authtoken/serializers.py:20
msgid "User account is disabled."
msgstr "Kullanıcı hesabı devre dışı bırakılmış."
#: authtoken/serializers.py:23
msgid "Unable to log in with provided credentials."
msgstr "Verilen bilgiler ile giriş sağlanamadı."
#: authtoken/serializers.py:26
msgid "Must include \"username\" and \"password\"."
msgstr "\"Kullanıcı Adı\" ve \"Parola\" eklenmeli."

Binary file not shown.

View File

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

Binary file not shown.

View File

@ -0,0 +1,325 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Lele Long <schemacs@gmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: Django REST framework\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-30 16:23+0000\n"
"PO-Revision-Date: 2015-01-30 16:27+0000\n"
"Last-Translator: Thomas Christie <tom@tomchristie.com>\n"
"Language-Team: Chinese (China) (http://www.transifex.com/projects/p/django-rest-framework/language/zh_CN/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh_CN\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: authentication.py:69
msgid "Invalid basic header. No credentials provided."
msgstr "没有提供认证信息基本认证HTTP头无效。"
#: authentication.py:72
msgid "Invalid basic header. Credentials string should not contain spaces."
msgstr "认证字符串不应该包含空格基本认证HTTP头无效。"
#: authentication.py:78
msgid "Invalid basic header. Credentials not correctly base64 encoded."
msgstr "认证字符串base64编码错误基本认证HTTP头无效。"
#: authentication.py:90
msgid "Invalid username/password."
msgstr "用户名或者密码错误。"
#: authentication.py:156
msgid "Invalid token header. No credentials provided."
msgstr "没有提供认证信息认证令牌HTTP头无效。"
#: authentication.py:159
msgid "Invalid token header. Token string should not contain spaces."
msgstr "认证令牌字符串不应该包含空格无效的认证令牌HTTP头。"
#: authentication.py:168
msgid "Invalid token."
msgstr "认证令牌无效。"
#: authentication.py:171
msgid "User inactive or deleted."
msgstr "用户未激活或者已删除。"
#: exceptions.py:38
msgid "A server error occurred."
msgstr "服务器出现了错误。"
#: exceptions.py:73
msgid "Malformed request."
msgstr "畸形的请求。"
#: exceptions.py:78
msgid "Incorrect authentication credentials."
msgstr "不正确的身份认证凭据。"
#: exceptions.py:83
msgid "Authentication credentials were not provided."
msgstr "身份认证凭据未提供。"
#: exceptions.py:88
msgid "You do not have permission to perform this action."
msgstr "您没有执行该操作的权限。"
#: exceptions.py:93
msgid "Not found."
msgstr "未找到。"
#: exceptions.py:98
msgid "Method \"{method}\" not allowed."
msgstr "方法 “{method}” 不被允许。"
#: exceptions.py:109
msgid "Could not satisfy the request Accept header."
msgstr "无法满足Accept HTTP头的请求。"
#: exceptions.py:121
msgid "Unsupported media type \"{media_type}\" in request."
msgstr "不支持请求中的媒体类型 “{media_type}”。"
#: exceptions.py:134
msgid "Request was throttled."
msgstr "请求被限速。"
#: fields.py:153 relations.py:132 relations.py:156 validators.py:77
#: validators.py:155
msgid "This field is required."
msgstr "这个字段是必填项。"
#: fields.py:154
msgid "This field may not be null."
msgstr "这个值不能为 null。"
#: fields.py:487 fields.py:515
msgid "\"{input}\" is not a valid boolean."
msgstr "“{input}” 不是合法的布尔值。"
#: fields.py:550
msgid "This field may not be blank."
msgstr "此字段不能为空。"
#: fields.py:551 fields.py:1324
msgid "Ensure this field has no more than {max_length} characters."
msgstr "请确保这个字段不能超过 {max_length} 个字符。"
#: fields.py:552
msgid "Ensure this field has at least {min_length} characters."
msgstr "请确保这个字段至少包含 {min_length} 个字符。"
#: fields.py:587
msgid "Enter a valid email address."
msgstr "请输入合法的邮件地址。"
#: fields.py:604
msgid "This value does not match the required pattern."
msgstr "输入值不匹配要求的模式。"
#: fields.py:615
msgid ""
"Enter a valid \"slug\" consisting of letters, numbers, underscores or "
"hyphens."
msgstr "请输入合法的“短语“,只能包含字母,数字,下划线或者中划线。"
#: fields.py:627
msgid "Enter a valid URL."
msgstr "请输入合法的URL。"
#: fields.py:638
msgid "\"{value}\" is not a valid UUID."
msgstr ""
#: fields.py:657
msgid "A valid integer is required."
msgstr "请填写合法的整数值。"
#: fields.py:658 fields.py:692 fields.py:725
msgid "Ensure this value is less than or equal to {max_value}."
msgstr "请确保该值小于或者等于 {max_value}。"
#: fields.py:659 fields.py:693 fields.py:726
msgid "Ensure this value is greater than or equal to {min_value}."
msgstr "请确保该值大于或者等于 {min_value}。"
#: fields.py:660 fields.py:694 fields.py:730
msgid "String value too large."
msgstr "字符值太长。"
#: fields.py:691 fields.py:724
msgid "A valid number is required."
msgstr "请填写合法的数字。"
#: fields.py:727
msgid "Ensure that there are no more than {max_digits} digits in total."
msgstr "请确保总计不超过 {max_digits} 个数字。"
#: fields.py:728
msgid ""
"Ensure that there are no more than {max_decimal_places} decimal places."
msgstr "请确保总计不超过 {max_decimal_places} 个小数位。"
#: fields.py:729
msgid ""
"Ensure that there are no more than {max_whole_digits} digits before the "
"decimal point."
msgstr "请确保小数点前不超过 {max_whole_digits} 个数字。"
#: fields.py:813
msgid "Datetime has wrong format. Use one of these formats instead: {format}."
msgstr "日期时间格式错误。请从这些格式中选择:{format}。"
#: fields.py:814
msgid "Expected a datetime but got a date."
msgstr "期望为日期时间,得到的是日期。"
#: fields.py:878
msgid "Date has wrong format. Use one of these formats instead: {format}."
msgstr "日期格式错误。请从这些格式中选择:{format}。"
#: fields.py:879
msgid "Expected a date but got a datetime."
msgstr "期望为日期,得到的是日期时间。"
#: fields.py:936
msgid "Time has wrong format. Use one of these formats instead: {format}."
msgstr "时间格式错误。请从这些格式中选择:{format}。"
#: fields.py:992 fields.py:1036
msgid "\"{input}\" is not a valid choice."
msgstr "“{input}” 不是合法选项。"
#: fields.py:1037 fields.py:1151 serializers.py:482
msgid "Expected a list of items but got type \"{input_type}\"."
msgstr "期望为一个包含物件的列表,得到的类型是“{input_type}”。"
#: fields.py:1067
msgid "No file was submitted."
msgstr "没有提交任何文件。"
#: fields.py:1068
msgid ""
"The submitted data was not a file. Check the encoding type on the form."
msgstr "提交的数据不是一个文件。请检查表单的编码类型。"
#: fields.py:1069
msgid "No filename could be determined."
msgstr "无法检测到文件名。"
#: fields.py:1070
msgid "The submitted file is empty."
msgstr "提交的是空文件。"
#: fields.py:1071
msgid ""
"Ensure this filename has at most {max_length} characters (it has {length})."
msgstr "确保该文件名最多包含 {max_length} 个字符 ( 当前长度为{length} ) 。"
#: fields.py:1113
msgid ""
"Upload a valid image. The file you uploaded was either not an image or a "
"corrupted image."
msgstr "请上传有效图片。您上传的该文件不是图片或者图片已经损坏。"
#: fields.py:1188
msgid "Expected a dictionary of items but got type \"{input_type}\"."
msgstr ""
#: pagination.py:221
msgid "Invalid page \"{page_number}\": {message}."
msgstr "无效页面 “{page_number}”:{message}。"
#: pagination.py:442
msgid "Invalid cursor"
msgstr ""
#: relations.py:133
msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "无效主键 “{pk_value}” 对象不存在。"
#: relations.py:134
msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "类型错误。期望为主键,得到的类型为 {data_type}。"
#: relations.py:157
msgid "Invalid hyperlink - No URL match."
msgstr "无效超链接 没有匹配的URL。"
#: relations.py:158
msgid "Invalid hyperlink - Incorrect URL match."
msgstr "无效超链接 错误的URL匹配。"
#: relations.py:159
msgid "Invalid hyperlink - Object does not exist."
msgstr "无效超链接 -对象不存在。"
#: relations.py:160
msgid "Incorrect type. Expected URL string, received {data_type}."
msgstr "类型错误。期望为URL字符串得到的类型是 {data_type}。"
#: relations.py:295
msgid "Object with {slug_name}={value} does not exist."
msgstr "属性 {slug_name} 为 {value} 的对象不存在。"
#: relations.py:296
msgid "Invalid value."
msgstr "无效值。"
#: serializers.py:299
msgid "Invalid data. Expected a dictionary, but got {datatype}."
msgstr "无效数据。期待为字典类型,得到的是 {datatype} 。"
#: validators.py:22
msgid "This field must be unique."
msgstr "该字段必须唯一。"
#: validators.py:76
msgid "The fields {field_names} must make a unique set."
msgstr "字段 {field_names} 必须能构成唯一集合。"
#: validators.py:219
msgid "This field must be unique for the \"{date_field}\" date."
msgstr "该字段必须在日期 “{date_field}” 唯一。"
#: validators.py:234
msgid "This field must be unique for the \"{date_field}\" month."
msgstr "该字段必须在月份 “{date_field}” 唯一。"
#: validators.py:247
msgid "This field must be unique for the \"{date_field}\" year."
msgstr "该字段必须在年 “{date_field}” 唯一。"
#: versioning.py:39
msgid "Invalid version in \"Accept\" header."
msgstr "“Accept” HTTP头包含无效版本。"
#: versioning.py:70 versioning.py:112
msgid "Invalid version in URL path."
msgstr "URl路径包含无效版本。"
#: versioning.py:138
msgid "Invalid version in hostname."
msgstr "主机名包含无效版本。"
#: versioning.py:160
msgid "Invalid version in query parameter."
msgstr "请求参数里包含无效版本。"
#: authtoken/serializers.py:20
msgid "User account is disabled."
msgstr "用户账户已禁用。"
#: authtoken/serializers.py:23
msgid "Unable to log in with provided credentials."
msgstr "无法使用提供的认证信息登录。"
#: authtoken/serializers.py:26
msgid "Must include \"username\" and \"password\"."
msgstr "必须包含 “用户名” 和 “密码”。"

View File

@ -5,7 +5,6 @@ We don't bind behaviour to http method handlers yet,
which allows mixin classes to be composed in interesting ways.
"""
from __future__ import unicode_literals
from rest_framework import status
from rest_framework.response import Response
from rest_framework.settings import api_settings
@ -37,12 +36,14 @@ class ListModelMixin(object):
List a queryset.
"""
def list(self, request, *args, **kwargs):
instance = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(instance)
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_pagination_serializer(page)
else:
serializer = self.get_serializer(instance, many=True)
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)

View File

@ -1,85 +1,684 @@
# coding: utf-8
"""
Pagination serializers determine the structure of the output that should
be used for paginated responses.
"""
from __future__ import unicode_literals
from rest_framework import serializers
from rest_framework.templatetags.rest_framework import replace_query_param
from base64 import b64encode, b64decode
from collections import namedtuple
from django.core.paginator import InvalidPage, Paginator as DjangoPaginator
from django.template import Context, loader
from django.utils import six
from django.utils.six.moves.urllib import parse as urlparse
from django.utils.translation import ugettext as _
from rest_framework.compat import OrderedDict
from rest_framework.exceptions import NotFound
from rest_framework.response import Response
from rest_framework.settings import api_settings
from rest_framework.utils.urls import (
replace_query_param, remove_query_param
)
class NextPageField(serializers.Field):
def _positive_int(integer_string, strict=False, cutoff=None):
"""
Field that returns a link to the next page in paginated results.
Cast a string to a strictly positive integer.
"""
page_field = 'page'
def to_representation(self, value):
if not value.has_next():
return None
page = value.next_page_number()
request = self.context.get('request')
url = request and request.build_absolute_uri() or ''
return replace_query_param(url, self.page_field, page)
ret = int(integer_string)
if ret < 0 or (ret == 0 and strict):
raise ValueError()
if cutoff:
ret = min(ret, cutoff)
return ret
class PreviousPageField(serializers.Field):
def _divide_with_ceil(a, b):
"""
Field that returns a link to the previous page in paginated results.
Returns 'a' divded by 'b', with any remainder rounded up.
"""
page_field = 'page'
def to_representation(self, value):
if not value.has_previous():
return None
page = value.previous_page_number()
request = self.context.get('request')
url = request and request.build_absolute_uri() or ''
return replace_query_param(url, self.page_field, page)
if a % b:
return (a // b) + 1
return a // b
class DefaultObjectSerializer(serializers.Serializer):
def _get_count(queryset):
"""
If no object serializer is specified, then this serializer will be applied
as the default.
Determine an object count, supporting either querysets or regular lists.
"""
def to_representation(self, value):
return value
try:
return queryset.count()
except (AttributeError, TypeError):
return len(queryset)
class BasePaginationSerializer(serializers.Serializer):
def _get_displayed_page_numbers(current, final):
"""
A base class for pagination serializers to inherit from,
to make implementing custom serializers more easy.
"""
results_field = 'results'
This utility function determines a list of page numbers to display.
This gives us a nice contextually relevant set of page numbers.
def __init__(self, *args, **kwargs):
For example:
current=14, final=16 -> [1, None, 13, 14, 15, 16]
This implementation gives one page to each side of the cursor,
or two pages to the side when the cursor is at the edge, then
ensures that any breaks between non-continous page numbers never
remove only a single page.
For an alernativative implementation which gives two pages to each side of
the cursor, eg. as in GitHub issue list pagination, see:
https://gist.github.com/tomchristie/321140cebb1c4a558b15
"""
assert current >= 1
assert final >= current
if final <= 5:
return list(range(1, final + 1))
# We always include the first two pages, last two pages, and
# two pages either side of the current page.
included = set((
1,
current - 1, current, current + 1,
final
))
# If the break would only exclude a single page number then we
# may as well include the page number instead of the break.
if current <= 4:
included.add(2)
included.add(3)
if current >= final - 3:
included.add(final - 1)
included.add(final - 2)
# Now sort the page numbers and drop anything outside the limits.
included = [
idx for idx in sorted(list(included))
if idx > 0 and idx <= final
]
# Finally insert any `...` breaks
if current > 4:
included.insert(1, None)
if current < final - 3:
included.insert(len(included) - 1, None)
return included
def _get_page_links(page_numbers, current, url_func):
"""
Given a list of page numbers and `None` page breaks,
return a list of `PageLink` objects.
"""
page_links = []
for page_number in page_numbers:
if page_number is None:
page_link = PAGE_BREAK
else:
page_link = PageLink(
url=url_func(page_number),
number=page_number,
is_active=(page_number == current),
is_break=False
)
page_links.append(page_link)
return page_links
def _decode_cursor(encoded):
"""
Given a string representing an encoded cursor, return a `Cursor` instance.
"""
try:
querystring = b64decode(encoded.encode('ascii')).decode('ascii')
tokens = urlparse.parse_qs(querystring, keep_blank_values=True)
offset = tokens.get('o', ['0'])[0]
offset = _positive_int(offset)
reverse = tokens.get('r', ['0'])[0]
reverse = bool(int(reverse))
position = tokens.get('p', [None])[0]
except (TypeError, ValueError):
return None
return Cursor(offset=offset, reverse=reverse, position=position)
def _encode_cursor(cursor):
"""
Given a Cursor instance, return an encoded string representation.
"""
tokens = {}
if cursor.offset != 0:
tokens['o'] = str(cursor.offset)
if cursor.reverse:
tokens['r'] = '1'
if cursor.position is not None:
tokens['p'] = cursor.position
querystring = urlparse.urlencode(tokens, doseq=True)
return b64encode(querystring.encode('ascii')).decode('ascii')
def _reverse_ordering(ordering_tuple):
"""
Given an order_by tuple such as `('-created', 'uuid')` reverse the
ordering and return a new tuple, eg. `('created', '-uuid')`.
"""
def invert(x):
return x[1:] if (x.startswith('-')) else '-' + x
return tuple([invert(item) for item in ordering_tuple])
Cursor = namedtuple('Cursor', ['offset', 'reverse', 'position'])
PageLink = namedtuple('PageLink', ['url', 'number', 'is_active', 'is_break'])
PAGE_BREAK = PageLink(url=None, number=None, is_active=False, is_break=True)
class BasePagination(object):
display_page_controls = False
def paginate_queryset(self, queryset, request, view=None): # pragma: no cover
raise NotImplementedError('paginate_queryset() must be implemented.')
def get_paginated_response(self, data): # pragma: no cover
raise NotImplementedError('get_paginated_response() must be implemented.')
def to_html(self): # pragma: no cover
raise NotImplementedError('to_html() must be implemented to display page controls.')
class PageNumberPagination(BasePagination):
"""
A simple page number based style that supports page numbers as
query parameters. For example:
http://api.example.org/accounts/?page=4
http://api.example.org/accounts/?page=4&page_size=100
"""
# The default page size.
# Defaults to `None`, meaning pagination is disabled.
paginate_by = api_settings.PAGINATE_BY
# Client can control the page using this query parameter.
page_query_param = 'page'
# Client can control the page size using this query parameter.
# Default is 'None'. Set to eg 'page_size' to enable usage.
paginate_by_param = api_settings.PAGINATE_BY_PARAM
# Set to an integer to limit the maximum page size the client may request.
# Only relevant if 'paginate_by_param' has also been set.
max_paginate_by = api_settings.MAX_PAGINATE_BY
last_page_strings = ('last',)
template = 'rest_framework/pagination/numbers.html'
invalid_page_message = _('Invalid page "{page_number}": {message}.')
def _handle_backwards_compat(self, view):
"""
Override init to add in the object serializer field on-the-fly.
Prior to version 3.1, pagination was handled in the view, and the
attributes were set there. The attributes should now be set on
the pagination class, but the old style is still pending deprecation.
"""
super(BasePaginationSerializer, self).__init__(*args, **kwargs)
results_field = self.results_field
for attr in (
'paginate_by', 'page_query_param',
'paginate_by_param', 'max_paginate_by'
):
if hasattr(view, attr):
setattr(self, attr, getattr(view, attr))
def paginate_queryset(self, queryset, request, view=None):
"""
Paginate a queryset if required, either returning a
page object, or `None` if pagination is not configured for this view.
"""
self._handle_backwards_compat(view)
page_size = self.get_page_size(request)
if not page_size:
return None
paginator = DjangoPaginator(queryset, page_size)
page_number = request.query_params.get(self.page_query_param, 1)
if page_number in self.last_page_strings:
page_number = paginator.num_pages
try:
object_serializer = self.Meta.object_serializer_class
except AttributeError:
object_serializer = DefaultObjectSerializer
self.page = paginator.page(page_number)
except InvalidPage as exc:
msg = self.invalid_page_message.format(
page_number=page_number, message=six.text_type(exc)
)
raise NotFound(msg)
if paginator.count > 1:
# The browsable API should display pagination controls.
self.display_page_controls = True
self.request = request
return self.page
def get_paginated_response(self, data):
return Response(OrderedDict([
('count', self.page.paginator.count),
('next', self.get_next_link()),
('previous', self.get_previous_link()),
('results', data)
]))
def get_page_size(self, request):
if self.paginate_by_param:
try:
return _positive_int(
request.query_params[self.paginate_by_param],
strict=True,
cutoff=self.max_paginate_by
)
except (KeyError, ValueError):
pass
return self.paginate_by
def get_next_link(self):
if not self.page.has_next():
return None
url = self.request.build_absolute_uri()
page_number = self.page.next_page_number()
return replace_query_param(url, self.page_query_param, page_number)
def get_previous_link(self):
if not self.page.has_previous():
return None
url = self.request.build_absolute_uri()
page_number = self.page.previous_page_number()
if page_number == 1:
return remove_query_param(url, self.page_query_param)
return replace_query_param(url, self.page_query_param, page_number)
def get_html_context(self):
base_url = self.request.build_absolute_uri()
def page_number_to_url(page_number):
if page_number == 1:
return remove_query_param(base_url, self.page_query_param)
else:
return replace_query_param(base_url, self.page_query_param, page_number)
current = self.page.number
final = self.page.paginator.num_pages
page_numbers = _get_displayed_page_numbers(current, final)
page_links = _get_page_links(page_numbers, current, page_number_to_url)
return {
'previous_url': self.get_previous_link(),
'next_url': self.get_next_link(),
'page_links': page_links
}
def to_html(self):
template = loader.get_template(self.template)
context = Context(self.get_html_context())
return template.render(context)
class LimitOffsetPagination(BasePagination):
"""
A limit/offset based style. For example:
http://api.example.org/accounts/?limit=100
http://api.example.org/accounts/?offset=400&limit=100
"""
default_limit = api_settings.PAGINATE_BY
limit_query_param = 'limit'
offset_query_param = 'offset'
max_limit = None
template = 'rest_framework/pagination/numbers.html'
def paginate_queryset(self, queryset, request, view=None):
self.limit = self.get_limit(request)
self.offset = self.get_offset(request)
self.count = _get_count(queryset)
self.request = request
if self.count > self.limit:
self.display_page_controls = True
return queryset[self.offset:self.offset + self.limit]
def get_paginated_response(self, data):
return Response(OrderedDict([
('count', self.count),
('next', self.get_next_link()),
('previous', self.get_previous_link()),
('results', data)
]))
def get_limit(self, request):
if self.limit_query_param:
try:
return _positive_int(
request.query_params[self.limit_query_param],
cutoff=self.max_limit
)
except (KeyError, ValueError):
pass
return self.default_limit
def get_offset(self, request):
try:
list_serializer_class = object_serializer.Meta.list_serializer_class
except AttributeError:
list_serializer_class = serializers.ListSerializer
return _positive_int(
request.query_params[self.offset_query_param],
)
except (KeyError, ValueError):
return 0
self.fields[results_field] = list_serializer_class(
child=object_serializer(*args, **kwargs),
source='object_list'
def get_next_link(self):
if self.offset + self.limit >= self.count:
return None
url = self.request.build_absolute_uri()
offset = self.offset + self.limit
return replace_query_param(url, self.offset_query_param, offset)
def get_previous_link(self):
if self.offset <= 0:
return None
url = self.request.build_absolute_uri()
if self.offset - self.limit <= 0:
return remove_query_param(url, self.offset_query_param)
offset = self.offset - self.limit
return replace_query_param(url, self.offset_query_param, offset)
def get_html_context(self):
base_url = self.request.build_absolute_uri()
current = _divide_with_ceil(self.offset, self.limit) + 1
# The number of pages is a little bit fiddly.
# We need to sum both the number of pages from current offset to end
# plus the number of pages up to the current offset.
# When offset is not strictly divisible by the limit then we may
# end up introducing an extra page as an artifact.
final = (
_divide_with_ceil(self.count - self.offset, self.limit) +
_divide_with_ceil(self.offset, self.limit)
)
def page_number_to_url(page_number):
if page_number == 1:
return remove_query_param(base_url, self.offset_query_param)
else:
offset = self.offset + ((page_number - current) * self.limit)
return replace_query_param(base_url, self.offset_query_param, offset)
class PaginationSerializer(BasePaginationSerializer):
"""
A default implementation of a pagination serializer.
"""
count = serializers.ReadOnlyField(source='paginator.count')
next = NextPageField(source='*')
previous = PreviousPageField(source='*')
page_numbers = _get_displayed_page_numbers(current, final)
page_links = _get_page_links(page_numbers, current, page_number_to_url)
return {
'previous_url': self.get_previous_link(),
'next_url': self.get_next_link(),
'page_links': page_links
}
def to_html(self):
template = loader.get_template(self.template)
context = Context(self.get_html_context())
return template.render(context)
class CursorPagination(BasePagination):
# Determine how/if True, False and None positions work - do the string
# encodings work with Django queryset filters?
# Consider a max offset cap.
# Tidy up the `get_ordering` API (eg remove queryset from it)
cursor_query_param = 'cursor'
page_size = api_settings.PAGINATE_BY
invalid_cursor_message = _('Invalid cursor')
ordering = None
template = 'rest_framework/pagination/previous_and_next.html'
def paginate_queryset(self, queryset, request, view=None):
self.base_url = request.build_absolute_uri()
self.ordering = self.get_ordering(request, queryset, view)
# Determine if we have a cursor, and if so then decode it.
encoded = request.query_params.get(self.cursor_query_param)
if encoded is None:
self.cursor = None
(offset, reverse, current_position) = (0, False, None)
else:
self.cursor = _decode_cursor(encoded)
if self.cursor is None:
raise NotFound(self.invalid_cursor_message)
(offset, reverse, current_position) = self.cursor
# Cursor pagination always enforces an ordering.
if reverse:
queryset = queryset.order_by(*_reverse_ordering(self.ordering))
else:
queryset = queryset.order_by(*self.ordering)
# If we have a cursor with a fixed position then filter by that.
if current_position is not None:
order = self.ordering[0]
is_reversed = order.startswith('-')
order_attr = order.lstrip('-')
# Test for: (cursor reversed) XOR (queryset reversed)
if self.cursor.reverse != is_reversed:
kwargs = {order_attr + '__lt': current_position}
else:
kwargs = {order_attr + '__gt': current_position}
queryset = queryset.filter(**kwargs)
# If we have an offset cursor then offset the entire page by that amount.
# We also always fetch an extra item in order to determine if there is a
# page following on from this one.
results = list(queryset[offset:offset + self.page_size + 1])
self.page = results[:self.page_size]
# Determine the position of the final item following the page.
if len(results) > len(self.page):
has_following_postion = True
following_position = self._get_position_from_instance(results[-1], self.ordering)
else:
has_following_postion = False
following_position = None
# If we have a reverse queryset, then the query ordering was in reverse
# so we need to reverse the items again before returning them to the user.
if reverse:
self.page = list(reversed(self.page))
if reverse:
# Determine next and previous positions for reverse cursors.
self.has_next = (current_position is not None) or (offset > 0)
self.has_previous = has_following_postion
if self.has_next:
self.next_position = current_position
if self.has_previous:
self.previous_position = following_position
else:
# Determine next and previous positions for forward cursors.
self.has_next = has_following_postion
self.has_previous = (current_position is not None) or (offset > 0)
if self.has_next:
self.next_position = following_position
if self.has_previous:
self.previous_position = current_position
# Display page controls in the browsable API if there is more
# than one page.
if self.has_previous or self.has_next:
self.display_page_controls = True
return self.page
def get_next_link(self):
if not self.has_next:
return None
if self.cursor and self.cursor.reverse and self.cursor.offset != 0:
# If we're reversing direction and we have an offset cursor
# then we cannot use the first position we find as a marker.
compare = self._get_position_from_instance(self.page[-1], self.ordering)
else:
compare = self.next_position
offset = 0
for item in reversed(self.page):
position = self._get_position_from_instance(item, self.ordering)
if position != compare:
# The item in this position and the item following it
# have different positions. We can use this position as
# our marker.
break
# The item in this postion has the same position as the item
# following it, we can't use it as a marker position, so increment
# the offset and keep seeking to the previous item.
compare = position
offset += 1
else:
# There were no unique positions in the page.
if not self.has_previous:
# We are on the first page.
# Our cursor will have an offset equal to the page size,
# but no position to filter against yet.
offset = self.page_size
position = None
elif self.cursor.reverse:
# The change in direction will introduce a paging artifact,
# where we end up skipping forward a few extra items.
offset = 0
position = self.previous_position
else:
# Use the position from the existing cursor and increment
# it's offset by the page size.
offset = self.cursor.offset + self.page_size
position = self.previous_position
cursor = Cursor(offset=offset, reverse=False, position=position)
encoded = _encode_cursor(cursor)
return replace_query_param(self.base_url, self.cursor_query_param, encoded)
def get_previous_link(self):
if not self.has_previous:
return None
if self.cursor and not self.cursor.reverse and self.cursor.offset != 0:
# If we're reversing direction and we have an offset cursor
# then we cannot use the first position we find as a marker.
compare = self._get_position_from_instance(self.page[0], self.ordering)
else:
compare = self.previous_position
offset = 0
for item in self.page:
position = self._get_position_from_instance(item, self.ordering)
if position != compare:
# The item in this position and the item following it
# have different positions. We can use this position as
# our marker.
break
# The item in this postion has the same position as the item
# following it, we can't use it as a marker position, so increment
# the offset and keep seeking to the previous item.
compare = position
offset += 1
else:
# There were no unique positions in the page.
if not self.has_next:
# We are on the final page.
# Our cursor will have an offset equal to the page size,
# but no position to filter against yet.
offset = self.page_size
position = None
elif self.cursor.reverse:
# Use the position from the existing cursor and increment
# it's offset by the page size.
offset = self.cursor.offset + self.page_size
position = self.next_position
else:
# The change in direction will introduce a paging artifact,
# where we end up skipping back a few extra items.
offset = 0
position = self.next_position
cursor = Cursor(offset=offset, reverse=True, position=position)
encoded = _encode_cursor(cursor)
return replace_query_param(self.base_url, self.cursor_query_param, encoded)
def get_ordering(self, request, queryset, view):
"""
Return a tuple of strings, that may be used in an `order_by` method.
"""
ordering_filters = [
filter_cls for filter_cls in getattr(view, 'filter_backends', [])
if hasattr(filter_cls, 'get_ordering')
]
if ordering_filters:
# If a filter exists on the view that implements `get_ordering`
# then we defer to that filter to determine the ordering.
filter_cls = ordering_filters[0]
filter_instance = filter_cls()
ordering = filter_instance.get_ordering(request, queryset, view)
assert ordering is not None, (
'Using cursor pagination, but filter class {filter_cls} '
'returned a `None` ordering.'.format(
filter_cls=filter_cls.__name__
)
)
else:
# The default case is to check for an `ordering` attribute,
# first on the view instance, and then on this pagination instance.
ordering = getattr(view, 'ordering', getattr(self, 'ordering', None))
assert ordering is not None, (
'Using cursor pagination, but no ordering attribute was declared '
'on the view or on the pagination class.'
)
assert isinstance(ordering, (six.string_types, list, tuple)), (
'Invalid ordering. Expected string or tuple, but got {type}'.format(
type=type(ordering).__name__
)
)
if isinstance(ordering, six.string_types):
return (ordering,)
return tuple(ordering)
def _get_position_from_instance(self, instance, ordering):
attr = getattr(instance, ordering[0].lstrip('-'))
return six.text_type(attr)
def get_paginated_response(self, data):
return Response(OrderedDict([
('next', self.get_next_link()),
('previous', self.get_previous_link()),
('results', data)
]))
def get_html_context(self):
return {
'previous_url': self.get_previous_link(),
'next_url': self.get_next_link()
}
def to_html(self):
template = loader.get_template(self.template)
context = Context(self.get_html_context())
return template.render(context)

View File

@ -14,12 +14,9 @@ from django.http.multipartparser import MultiPartParserError, parse_header, Chun
from django.utils import six
from django.utils.six.moves.urllib import parse as urlparse
from django.utils.encoding import force_text
from rest_framework.compat import etree, yaml
from rest_framework.exceptions import ParseError
from rest_framework import renderers
import json
import datetime
import decimal
class DataAndFiles(object):
@ -67,29 +64,6 @@ class JSONParser(BaseParser):
raise ParseError('JSON parse error - %s' % six.text_type(exc))
class YAMLParser(BaseParser):
"""
Parses YAML-serialized data.
"""
media_type = 'application/yaml'
def parse(self, stream, media_type=None, parser_context=None):
"""
Parses the incoming bytestream as YAML and returns the resulting data.
"""
assert yaml, 'YAMLParser requires pyyaml to be installed'
parser_context = parser_context or {}
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
try:
data = stream.read().decode(encoding)
return yaml.safe_load(data)
except (ValueError, yaml.parser.ParserError) as exc:
raise ParseError('YAML parse error - %s' % six.text_type(exc))
class FormParser(BaseParser):
"""
Parser for form data.
@ -138,78 +112,6 @@ class MultiPartParser(BaseParser):
raise ParseError('Multipart form parse error - %s' % six.text_type(exc))
class XMLParser(BaseParser):
"""
XML parser.
"""
media_type = 'application/xml'
def parse(self, stream, media_type=None, parser_context=None):
"""
Parses the incoming bytestream as XML and returns the resulting data.
"""
assert etree, 'XMLParser requires defusedxml to be installed'
parser_context = parser_context or {}
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
parser = etree.DefusedXMLParser(encoding=encoding)
try:
tree = etree.parse(stream, parser=parser, forbid_dtd=True)
except (etree.ParseError, ValueError) as exc:
raise ParseError('XML parse error - %s' % six.text_type(exc))
data = self._xml_convert(tree.getroot())
return data
def _xml_convert(self, element):
"""
convert the xml `element` into the corresponding python object
"""
children = list(element)
if len(children) == 0:
return self._type_convert(element.text)
else:
# if the fist child tag is list-item means all children are list-item
if children[0].tag == "list-item":
data = []
for child in children:
data.append(self._xml_convert(child))
else:
data = {}
for child in children:
data[child.tag] = self._xml_convert(child)
return data
def _type_convert(self, value):
"""
Converts the value returned by the XMl parse into the equivalent
Python type
"""
if value is None:
return value
try:
return datetime.datetime.strptime(value, '%Y-%m-%d %H:%M:%S')
except ValueError:
pass
try:
return int(value)
except ValueError:
pass
try:
return decimal.Decimal(value)
except decimal.InvalidOperation:
pass
return value
class FileUploadParser(BaseParser):
"""
Parser for file upload data.

View File

@ -3,8 +3,7 @@ Provides a set of pluggable permission policies.
"""
from __future__ import unicode_literals
from django.http import Http404
from rest_framework.compat import (get_model_name, oauth2_provider_scope,
oauth2_constants)
from rest_framework.compat import get_model_name
SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS']
@ -199,28 +198,3 @@ class DjangoObjectPermissions(DjangoModelPermissions):
return False
return True
class TokenHasReadWriteScope(BasePermission):
"""
The request is authenticated as a user and the token used has the right scope
"""
def has_permission(self, request, view):
token = request.auth
read_only = request.method in SAFE_METHODS
if not token:
return False
if hasattr(token, 'resource'): # OAuth 1
return read_only or not request.auth.resource.is_readonly
elif hasattr(token, 'scope'): # OAuth 2
required = oauth2_constants.READ if read_only else oauth2_constants.WRITE
return oauth2_provider_scope.check(required, request.auth.scope)
assert False, (
'TokenHasReadWriteScope requires either the'
'`OAuthAuthentication` or `OAuth2Authentication` authentication '
'class to be used.'
)

View File

@ -1,7 +1,7 @@
# coding: utf-8
from __future__ import unicode_literals
from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
from django.core.urlresolvers import resolve, get_script_prefix, NoReverseMatch, Resolver404
from django.core.urlresolvers import get_script_prefix, resolve, NoReverseMatch, Resolver404
from django.db.models.query import QuerySet
from django.utils import six
from django.utils.encoding import smart_text
@ -130,7 +130,7 @@ class StringRelatedField(RelatedField):
class PrimaryKeyRelatedField(RelatedField):
default_error_messages = {
'required': _('This field is required.'),
'does_not_exist': _("Invalid pk '{pk_value}' - object does not exist."),
'does_not_exist': _('Invalid pk "{pk_value}" - object does not exist.'),
'incorrect_type': _('Incorrect type. Expected pk value, received {data_type}.'),
}
@ -154,7 +154,7 @@ class HyperlinkedRelatedField(RelatedField):
default_error_messages = {
'required': _('This field is required.'),
'no_match': _('Invalid hyperlink - No URL match'),
'no_match': _('Invalid hyperlink - No URL match.'),
'incorrect_match': _('Invalid hyperlink - Incorrect URL match.'),
'does_not_exist': _('Invalid hyperlink - Object does not exist.'),
'incorrect_type': _('Incorrect type. Expected URL string, received {data_type}.'),
@ -167,11 +167,10 @@ class HyperlinkedRelatedField(RelatedField):
self.lookup_url_kwarg = kwargs.pop('lookup_url_kwarg', self.lookup_field)
self.format = kwargs.pop('format', None)
# We include these simply for dependency injection in tests.
# We can't add them as class attributes or they would expect an
# We include this simply for dependency injection in tests.
# We can't add it as a class attributes or it would expect an
# implicit `self` argument to be passed.
self.reverse = reverse
self.resolve = resolve
super(HyperlinkedRelatedField, self).__init__(**kwargs)
@ -205,6 +204,7 @@ class HyperlinkedRelatedField(RelatedField):
return self.reverse(view_name, kwargs=kwargs, request=request, format=format)
def to_internal_value(self, data):
request = self.context.get('request', None)
try:
http_prefix = data.startswith(('http:', 'https:'))
except AttributeError:
@ -218,11 +218,18 @@ class HyperlinkedRelatedField(RelatedField):
data = '/' + data[len(prefix):]
try:
match = self.resolve(data)
match = resolve(data)
except Resolver404:
self.fail('no_match')
if match.view_name != self.view_name:
try:
expected_viewname = request.versioning_scheme.get_versioned_viewname(
self.view_name, request
)
except AttributeError:
expected_viewname = self.view_name
if match.view_name != expected_viewname:
self.fail('incorrect_match')
try:
@ -292,7 +299,7 @@ class SlugRelatedField(RelatedField):
"""
default_error_messages = {
'does_not_exist': _("Object with {slug_name}={value} does not exist."),
'does_not_exist': _('Object with {slug_name}={value} does not exist.'),
'invalid': _('Invalid value.'),
}

View File

@ -17,11 +17,8 @@ from django.http.multipartparser import parse_header
from django.template import Context, RequestContext, loader, Template
from django.test.client import encode_multipart
from django.utils import six
from django.utils.encoding import smart_text
from django.utils.xmlutils import SimplerXMLGenerator
from django.utils.six.moves import StringIO
from rest_framework import exceptions, serializers, status, VERSION
from rest_framework.compat import SHORT_SEPARATORS, LONG_SEPARATORS, yaml
from rest_framework.compat import SHORT_SEPARATORS, LONG_SEPARATORS, INDENT_SEPARATORS
from rest_framework.exceptions import ParseError
from rest_framework.settings import api_settings
from rest_framework.request import is_form_media_type, override_method
@ -90,7 +87,11 @@ class JSONRenderer(BaseRenderer):
renderer_context = renderer_context or {}
indent = self.get_indent(accepted_media_type, renderer_context)
separators = SHORT_SEPARATORS if (indent is None and self.compact) else LONG_SEPARATORS
if indent is None:
separators = SHORT_SEPARATORS if self.compact else LONG_SEPARATORS
else:
separators = INDENT_SEPARATORS
ret = json.dumps(
data, cls=self.encoder_class,
@ -112,112 +113,6 @@ class JSONRenderer(BaseRenderer):
return ret
class JSONPRenderer(JSONRenderer):
"""
Renderer which serializes to json,
wrapping the json output in a callback function.
"""
media_type = 'application/javascript'
format = 'jsonp'
callback_parameter = 'callback'
default_callback = 'callback'
charset = 'utf-8'
def get_callback(self, renderer_context):
"""
Determine the name of the callback to wrap around the json output.
"""
request = renderer_context.get('request', None)
params = request and request.query_params or {}
return params.get(self.callback_parameter, self.default_callback)
def render(self, data, accepted_media_type=None, renderer_context=None):
"""
Renders into jsonp, wrapping the json output in a callback function.
Clients may set the callback function name using a query parameter
on the URL, for example: ?callback=exampleCallbackName
"""
renderer_context = renderer_context or {}
callback = self.get_callback(renderer_context)
json = super(JSONPRenderer, self).render(data, accepted_media_type,
renderer_context)
return callback.encode(self.charset) + b'(' + json + b');'
class XMLRenderer(BaseRenderer):
"""
Renderer which serializes to XML.
"""
media_type = 'application/xml'
format = 'xml'
charset = 'utf-8'
def render(self, data, accepted_media_type=None, renderer_context=None):
"""
Renders `data` into serialized XML.
"""
if data is None:
return ''
stream = StringIO()
xml = SimplerXMLGenerator(stream, self.charset)
xml.startDocument()
xml.startElement("root", {})
self._to_xml(xml, data)
xml.endElement("root")
xml.endDocument()
return stream.getvalue()
def _to_xml(self, xml, data):
if isinstance(data, (list, tuple)):
for item in data:
xml.startElement("list-item", {})
self._to_xml(xml, item)
xml.endElement("list-item")
elif isinstance(data, dict):
for key, value in six.iteritems(data):
xml.startElement(key, {})
self._to_xml(xml, value)
xml.endElement(key)
elif data is None:
# Don't output any value
pass
else:
xml.characters(smart_text(data))
class YAMLRenderer(BaseRenderer):
"""
Renderer which serializes to YAML.
"""
media_type = 'application/yaml'
format = 'yaml'
encoder = encoders.SafeDumper
charset = 'utf-8'
ensure_ascii = False
def render(self, data, accepted_media_type=None, renderer_context=None):
"""
Renders `data` into serialized YAML.
"""
assert yaml, 'YAMLRenderer requires pyyaml to be installed'
if data is None:
return ''
return yaml.dump(data, stream=None, encoding=self.charset, Dumper=self.encoder, allow_unicode=not self.ensure_ascii)
class TemplateHTMLRenderer(BaseRenderer):
"""
An HTML renderer for use with templates.
@ -696,6 +591,11 @@ class BrowsableAPIRenderer(BaseRenderer):
renderer_content_type += ' ;%s' % renderer.charset
response_headers['Content-Type'] = renderer_content_type
if hasattr(view, 'paginator') and view.paginator.display_page_controls:
paginator = view.paginator
else:
paginator = None
context = {
'content': self.get_content(renderer, data, accepted_media_type, renderer_context),
'view': view,
@ -704,6 +604,7 @@ class BrowsableAPIRenderer(BaseRenderer):
'description': self.get_description(view),
'name': self.get_name(view),
'version': VERSION,
'paginator': paginator,
'breadcrumblist': self.get_breadcrumbs(request),
'allowed_methods': view.allowed_methods,
'available_formats': [renderer_cls.format for renderer_cls in view.renderer_classes],

View File

@ -108,6 +108,10 @@ def clone_request(request, method):
ret.accepted_renderer = request.accepted_renderer
if hasattr(request, 'accepted_media_type'):
ret.accepted_media_type = request.accepted_media_type
if hasattr(request, 'version'):
ret.version = request.version
if hasattr(request, 'versioning_scheme'):
ret.versioning_scheme = request.versioning_scheme
return ret
@ -216,8 +220,8 @@ class Request(object):
Synonym for `.query_params`, for backwards compatibility.
"""
warnings.warn(
"`request.QUERY_PARAMS` is pending deprecation. Use `request.query_params` instead.",
PendingDeprecationWarning,
"`request.QUERY_PARAMS` is deprecated. Use `request.query_params` instead.",
DeprecationWarning,
stacklevel=1
)
return self._request.GET
@ -237,8 +241,8 @@ class Request(object):
arbitrary parsers, and also works on methods other than POST (eg PUT).
"""
warnings.warn(
"`request.DATA` is pending deprecation. Use `request.data` instead.",
PendingDeprecationWarning,
"`request.DATA` is deprecated. Use `request.data` instead.",
DeprecationWarning,
stacklevel=1
)
if not _hasattr(self, '_data'):
@ -254,8 +258,8 @@ class Request(object):
arbitrary parsers, and also works on methods other than POST (eg PUT).
"""
warnings.warn(
"`request.FILES` is pending deprecation. Use `request.data` instead.",
PendingDeprecationWarning,
"`request.FILES` is deprecated. Use `request.data` instead.",
DeprecationWarning,
stacklevel=1
)
if not _hasattr(self, '_files'):

View File

@ -1,5 +1,5 @@
"""
Provide reverse functions that return fully qualified URLs
Provide urlresolver functions that return fully qualified URLs or view names
"""
from __future__ import unicode_literals
from django.core.urlresolvers import reverse as django_reverse
@ -8,6 +8,18 @@ from django.utils.functional import lazy
def reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra):
"""
If versioning is being used then we pass any `reverse` calls through
to the versioning scheme instance, so that the resulting URL
can be modified if needed.
"""
scheme = getattr(request, 'versioning_scheme', None)
if scheme is not None:
return scheme.reverse(viewname, args, kwargs, request, format, **extra)
return _reverse(viewname, args, kwargs, request, format, **extra)
def _reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra):
"""
Same as `django.core.urlresolvers.reverse`, but optionally takes a request
and returns a fully qualified URL, using the request to get the base URL.

View File

@ -12,7 +12,7 @@ response content is handled by parsers and renderers.
"""
from __future__ import unicode_literals
from django.db import models
from django.db.models.fields import FieldDoesNotExist, Field as DjangoField
from django.db.models.fields import FieldDoesNotExist, Field as DjangoModelField
from django.utils.translation import ugettext_lazy as _
from rest_framework.compat import postgres_fields, unicode_to_repr
from rest_framework.utils import model_meta
@ -327,7 +327,9 @@ class Serializer(BaseSerializer):
Returns a list of validator callables.
"""
# Used by the lazily-evaluated `validators` property.
return getattr(getattr(self, 'Meta', None), 'validators', [])
meta = getattr(self, 'Meta', None)
validators = getattr(meta, 'validators', None)
return validators[:] if validators else []
def get_initial(self):
if hasattr(self, 'initial_data'):
@ -477,7 +479,7 @@ class ListSerializer(BaseSerializer):
many = True
default_error_messages = {
'not_a_list': _('Expected a list of items but got type `{input_type}`.')
'not_a_list': _('Expected a list of items but got type "{input_type}".')
}
def __init__(self, *args, **kwargs):
@ -702,8 +704,7 @@ class ModelSerializer(Serializer):
you need you should either declare the extra/differing fields explicitly on
the serializer class, or simply use a `Serializer` class.
"""
_field_mapping = ClassLookupDict({
serializer_field_mapping = {
models.AutoField: IntegerField,
models.BigIntegerField: IntegerField,
models.BooleanField: BooleanField,
@ -725,10 +726,13 @@ class ModelSerializer(Serializer):
models.SmallIntegerField: IntegerField,
models.TextField: CharField,
models.TimeField: TimeField,
models.URLField: URLField
# Note: Some version-specific mappings also defined below.
})
_related_class = PrimaryKeyRelatedField
models.URLField: URLField,
}
serializer_related_field = PrimaryKeyRelatedField
serializer_url_field = HyperlinkedIdentityField
serializer_choice_field = ChoiceField
# Default `create` and `update` behavior...
def create(self, validated_data):
"""
@ -799,69 +803,82 @@ class ModelSerializer(Serializer):
return instance
def get_validators(self):
# If the validators have been declared explicitly then use that.
validators = getattr(getattr(self, 'Meta', None), 'validators', None)
if validators is not None:
return validators
# Determine the default set of validators.
validators = []
model_class = self.Meta.model
field_names = set([
field.source for field in self.fields.values()
if (field.source != '*') and ('.' not in field.source)
])
# Note that we make sure to check `unique_together` both on the
# base model class, but also on any parent classes.
for parent_class in [model_class] + list(model_class._meta.parents.keys()):
for unique_together in parent_class._meta.unique_together:
if field_names.issuperset(set(unique_together)):
validator = UniqueTogetherValidator(
queryset=parent_class._default_manager,
fields=unique_together
)
validators.append(validator)
# Add any unique_for_date/unique_for_month/unique_for_year constraints.
info = model_meta.get_field_info(model_class)
for field_name, field in info.fields_and_pk.items():
if field.unique_for_date and field_name in field_names:
validator = UniqueForDateValidator(
queryset=model_class._default_manager,
field=field_name,
date_field=field.unique_for_date
)
validators.append(validator)
if field.unique_for_month and field_name in field_names:
validator = UniqueForMonthValidator(
queryset=model_class._default_manager,
field=field_name,
date_field=field.unique_for_month
)
validators.append(validator)
if field.unique_for_year and field_name in field_names:
validator = UniqueForYearValidator(
queryset=model_class._default_manager,
field=field_name,
date_field=field.unique_for_year
)
validators.append(validator)
return validators
# Determine the fields to apply...
def get_fields(self):
declared_fields = copy.deepcopy(self._declared_fields)
"""
Return the dict of field names -> field instances that should be
used for `self.fields` when instantiating the serializer.
"""
assert hasattr(self, 'Meta'), (
'Class {serializer_class} missing "Meta" attribute'.format(
serializer_class=self.__class__.__name__
)
)
assert hasattr(self.Meta, 'model'), (
'Class {serializer_class} missing "Meta.model" attribute'.format(
serializer_class=self.__class__.__name__
)
)
ret = OrderedDict()
declared_fields = copy.deepcopy(self._declared_fields)
model = getattr(self.Meta, 'model')
depth = getattr(self.Meta, 'depth', 0)
if depth is not None:
assert depth >= 0, "'depth' may not be negative."
assert depth <= 10, "'depth' may not be greater than 10."
# Retrieve metadata about fields & relationships on the model class.
info = model_meta.get_field_info(model)
field_names = self.get_field_names(declared_fields, info)
# Determine any extra field arguments and hidden fields that
# should be included
extra_kwargs = self.get_extra_kwargs()
extra_kwargs, hidden_fields = self.get_uniqueness_extra_kwargs(
field_names, declared_fields, extra_kwargs
)
# Determine the fields that should be included on the serializer.
fields = OrderedDict()
for field_name in field_names:
# If the field is explicitly declared on the class then use that.
if field_name in declared_fields:
fields[field_name] = declared_fields[field_name]
continue
# Determine the serializer field class and keyword arguments.
field_class, field_kwargs = self.build_field(
field_name, info, model, depth
)
# Include any kwargs defined in `Meta.extra_kwargs`
extra_field_kwargs = extra_kwargs.get(field_name, {})
field_kwargs = self.include_extra_kwargs(
field_kwargs, extra_field_kwargs
)
# Create the serializer field.
fields[field_name] = field_class(**field_kwargs)
# Add in any hidden fields.
fields.update(hidden_fields)
return fields
# Methods for determining the set of field names to include...
def get_field_names(self, declared_fields, info):
"""
Returns the list of all field names that should be created when
instantiating this serializer class. This is based on the default
set of fields, but also takes into account the `Meta.fields` or
`Meta.exclude` options if they have been specified.
"""
fields = getattr(self.Meta, 'fields', None)
exclude = getattr(self.Meta, 'exclude', None)
depth = getattr(self.Meta, 'depth', 0)
extra_kwargs = getattr(self.Meta, 'extra_kwargs', {})
if fields and not isinstance(fields, (list, tuple)):
raise TypeError(
@ -875,75 +892,274 @@ class ModelSerializer(Serializer):
type(exclude).__name__
)
assert not (fields and exclude), "Cannot set both 'fields' and 'exclude'."
extra_kwargs = self._include_additional_options(extra_kwargs)
# Retrieve metadata about fields & relationships on the model class.
info = model_meta.get_field_info(model)
if fields is None:
# Use the default set of field names if none is supplied explicitly.
fields = self._get_default_field_names(declared_fields, info)
exclude = getattr(self.Meta, 'exclude', None)
if exclude is not None:
for field_name in exclude:
assert field_name in fields, (
'The field in the `exclude` option must be a model field. Got %s.' %
field_name
)
fields.remove(field_name)
else:
# Check that any fields declared on the class are
# also explicitly included in `Meta.fields`.
# Note that we ignore any fields that were declared on a parent
# class, in order to support only including a subset of fields
# when subclassing serializers.
declared_field_names = set(declared_fields.keys())
for cls in self.__class__.__bases__:
declared_field_names -= set(getattr(cls, '_declared_fields', []))
missing_fields = declared_field_names - set(fields)
assert not missing_fields, (
'Field `%s` has been declared on serializer `%s`, but '
'is missing from `Meta.fields`.' %
(list(missing_fields)[0], self.__class__.__name__)
assert not (fields and exclude), (
"Cannot set both 'fields' and 'exclude' options on "
"serializer {serializer_class}.".format(
serializer_class=self.__class__.__name__
)
)
# Determine the set of model fields, and the fields that they map to.
# We actually only need this to deal with the slightly awkward case
# of supporting `unique_for_date`/`unique_for_month`/`unique_for_year`.
model_field_mapping = {}
for field_name in fields:
if field_name in declared_fields:
field = declared_fields[field_name]
source = field.source or field_name
if fields is not None:
# Ensure that all declared fields have also been included in the
# `Meta.fields` option.
# Do not require any fields that are declared a parent class,
# in order to allow serializer subclasses to only include
# a subset of fields.
required_field_names = set(declared_fields)
for cls in self.__class__.__bases__:
required_field_names -= set(getattr(cls, '_declared_fields', []))
for field_name in required_field_names:
assert field_name in fields, (
"The field '{field_name}' was declared on serializer "
"{serializer_class}, but has not been included in the "
"'fields' option.".format(
field_name=field_name,
serializer_class=self.__class__.__name__
)
)
return fields
# Use the default set of field names if `Meta.fields` is not specified.
fields = self.get_default_field_names(declared_fields, info)
if exclude is not None:
# If `Meta.exclude` is included, then remove those fields.
for field_name in exclude:
assert field_name in fields, (
"The field '{field_name}' was include on serializer "
"{serializer_class} in the 'exclude' option, but does "
"not match any model field.".format(
field_name=field_name,
serializer_class=self.__class__.__name__
)
)
fields.remove(field_name)
return fields
def get_default_field_names(self, declared_fields, model_info):
"""
Return the default list of field names that will be used if the
`Meta.fields` option is not specified.
"""
return (
[model_info.pk.name] +
list(declared_fields.keys()) +
list(model_info.fields.keys()) +
list(model_info.forward_relations.keys())
)
# Methods for constructing serializer fields...
def build_field(self, field_name, info, model_class, nested_depth):
"""
Return a two tuple of (cls, kwargs) to build a serializer field with.
"""
if field_name in info.fields_and_pk:
model_field = info.fields_and_pk[field_name]
return self.build_standard_field(field_name, model_field)
elif field_name in info.relations:
relation_info = info.relations[field_name]
if not nested_depth:
return self.build_relational_field(field_name, relation_info)
else:
try:
source = extra_kwargs[field_name]['source']
except KeyError:
source = field_name
# Model fields will always have a simple source mapping,
# they can't be nested attribute lookups.
if '.' not in source and source != '*':
model_field_mapping[source] = field_name
return self.build_nested_field(field_name, relation_info, nested_depth)
elif hasattr(model_class, field_name):
return self.build_property_field(field_name, model_class)
elif field_name == api_settings.URL_FIELD_NAME:
return self.build_url_field(field_name, model_class)
return self.build_unknown_field(field_name, model_class)
def build_standard_field(self, field_name, model_field):
"""
Create regular model fields.
"""
field_mapping = ClassLookupDict(self.serializer_field_mapping)
field_class = field_mapping[model_field]
field_kwargs = get_field_kwargs(field_name, model_field)
if 'choices' in field_kwargs:
# Fields with choices get coerced into `ChoiceField`
# instead of using their regular typed field.
field_class = self.serializer_choice_field
if not issubclass(field_class, ModelField):
# `model_field` is only valid for the fallback case of
# `ModelField`, which is used when no other typed field
# matched to the model field.
field_kwargs.pop('model_field', None)
if not issubclass(field_class, CharField) and not issubclass(field_class, ChoiceField):
# `allow_blank` is only valid for textual fields.
field_kwargs.pop('allow_blank', None)
if postgres_fields and isinstance(model_field, postgres_fields.ArrayField):
# Populate the `child` argument on `ListField` instances generated
# for the PostgrSQL specfic `ArrayField`.
child_model_field = model_field.base_field
child_field_class, child_field_kwargs = self.build_standard_field(
'child', child_model_field
)
field_kwargs['child'] = child_field_class(**child_field_kwargs)
return field_class, field_kwargs
def build_relational_field(self, field_name, relation_info):
"""
Create fields for forward and reverse relationships.
"""
field_class = self.serializer_related_field
field_kwargs = get_relation_kwargs(field_name, relation_info)
# `view_name` is only valid for hyperlinked relationships.
if not issubclass(field_class, HyperlinkedRelatedField):
field_kwargs.pop('view_name', None)
return field_class, field_kwargs
def build_nested_field(self, field_name, relation_info, nested_depth):
"""
Create nested fields for forward and reverse relationships.
"""
class NestedSerializer(ModelSerializer):
class Meta:
model = relation_info.related_model
depth = nested_depth
field_class = NestedSerializer
field_kwargs = get_nested_relation_kwargs(relation_info)
return field_class, field_kwargs
def build_property_field(self, field_name, model_class):
"""
Create a read only field for model methods and properties.
"""
field_class = ReadOnlyField
field_kwargs = {}
return field_class, field_kwargs
def build_url_field(self, field_name, model_class):
"""
Create a field representing the object's own URL.
"""
field_class = self.serializer_url_field
field_kwargs = get_url_kwargs(model_class)
return field_class, field_kwargs
def build_unknown_field(self, field_name, model_class):
"""
Raise an error on any unknown fields.
"""
raise ImproperlyConfigured(
'Field name `%s` is not valid for model `%s`.' %
(field_name, model_class.__name__)
)
def include_extra_kwargs(self, kwargs, extra_kwargs):
"""
Include any 'extra_kwargs' that have been included for this field,
possibly removing any incompatible existing keyword arguments.
"""
if extra_kwargs.get('read_only', False):
for attr in [
'required', 'default', 'allow_blank', 'allow_null',
'min_length', 'max_length', 'min_value', 'max_value',
'validators', 'queryset'
]:
kwargs.pop(attr, None)
if extra_kwargs.get('default') and kwargs.get('required') is False:
kwargs.pop('required')
kwargs.update(extra_kwargs)
return kwargs
# Methods for determining additional keyword arguments to apply...
def get_extra_kwargs(self):
"""
Return a dictionary mapping field names to a dictionary of
additional keyword arguments.
"""
extra_kwargs = getattr(self.Meta, 'extra_kwargs', {})
read_only_fields = getattr(self.Meta, 'read_only_fields', None)
if read_only_fields is not None:
for field_name in read_only_fields:
kwargs = extra_kwargs.get(field_name, {})
kwargs['read_only'] = True
extra_kwargs[field_name] = kwargs
# These are all pending deprecation.
write_only_fields = getattr(self.Meta, 'write_only_fields', None)
if write_only_fields is not None:
warnings.warn(
"The `Meta.write_only_fields` option is deprecated. "
"Use `Meta.extra_kwargs={<field_name>: {'write_only': True}}` instead.",
DeprecationWarning,
stacklevel=3
)
for field_name in write_only_fields:
kwargs = extra_kwargs.get(field_name, {})
kwargs['write_only'] = True
extra_kwargs[field_name] = kwargs
view_name = getattr(self.Meta, 'view_name', None)
if view_name is not None:
warnings.warn(
"The `Meta.view_name` option is deprecated. "
"Use `Meta.extra_kwargs={'url': {'view_name': ...}}` instead.",
DeprecationWarning,
stacklevel=3
)
kwargs = extra_kwargs.get(api_settings.URL_FIELD_NAME, {})
kwargs['view_name'] = view_name
extra_kwargs[api_settings.URL_FIELD_NAME] = kwargs
lookup_field = getattr(self.Meta, 'lookup_field', None)
if lookup_field is not None:
warnings.warn(
"The `Meta.lookup_field` option is deprecated. "
"Use `Meta.extra_kwargs={'url': {'lookup_field': ...}}` instead.",
DeprecationWarning,
stacklevel=3
)
kwargs = extra_kwargs.get(api_settings.URL_FIELD_NAME, {})
kwargs['lookup_field'] = lookup_field
extra_kwargs[api_settings.URL_FIELD_NAME] = kwargs
return extra_kwargs
def get_uniqueness_extra_kwargs(self, field_names, declared_fields, extra_kwargs):
"""
Return any additional field options that need to be included as a
result of uniqueness constraints on the model. This is returned as
a two-tuple of:
('dict of updated extra kwargs', 'mapping of hidden fields')
"""
model = getattr(self.Meta, 'model')
model_fields = self._get_model_fields(
field_names, declared_fields, extra_kwargs
)
# Determine if we need any additional `HiddenField` or extra keyword
# arguments to deal with `unique_for` dates that are required to
# be in the input data in order to validate it.
hidden_fields = {}
unique_constraint_names = set()
for model_field_name, field_name in model_field_mapping.items():
try:
model_field = model._meta.get_field(model_field_name)
except FieldDoesNotExist:
continue
if not isinstance(model_field, DjangoField):
continue
for model_field in model_fields.values():
# Include each of the `unique_for_*` field names.
unique_constraint_names |= set([
model_field.unique_for_date,
@ -957,12 +1173,15 @@ class ModelSerializer(Serializer):
# so long as all the field names are included on the serializer.
for parent_class in [model] + list(model._meta.parents.keys()):
for unique_together_list in parent_class._meta.unique_together:
if set(fields).issuperset(set(unique_together_list)):
if set(field_names).issuperset(set(unique_together_list)):
unique_constraint_names |= set(unique_together_list)
# Now we have all the field names that have uniqueness constraints
# applied, we can add the extra 'required=...' or 'default=...'
# arguments that are appropriate to these fields, or add a `HiddenField` for it.
hidden_fields = {}
uniqueness_extra_kwargs = {}
for unique_constraint_name in unique_constraint_names:
# Get the model field that is referred too.
unique_constraint_field = model._meta.get_field(unique_constraint_name)
@ -976,172 +1195,161 @@ class ModelSerializer(Serializer):
else:
default = empty
if unique_constraint_name in model_field_mapping:
if unique_constraint_name in model_fields:
# The corresponding field is present in the serializer
if unique_constraint_name not in extra_kwargs:
extra_kwargs[unique_constraint_name] = {}
if default is empty:
if 'required' not in extra_kwargs[unique_constraint_name]:
extra_kwargs[unique_constraint_name]['required'] = True
uniqueness_extra_kwargs[unique_constraint_name] = {'required': True}
else:
if 'default' not in extra_kwargs[unique_constraint_name]:
extra_kwargs[unique_constraint_name]['default'] = default
uniqueness_extra_kwargs[unique_constraint_name] = {'default': default}
elif default is not empty:
# The corresponding field is not present in the,
# serializer. We have a default to use for it, so
# add in a hidden field that populates it.
hidden_fields[unique_constraint_name] = HiddenField(default=default)
# Now determine the fields that should be included on the serializer.
for field_name in fields:
# Update `extra_kwargs` with any new options.
for key, value in uniqueness_extra_kwargs.items():
if key in extra_kwargs:
extra_kwargs[key].update(value)
else:
extra_kwargs[key] = value
return extra_kwargs, hidden_fields
def _get_model_fields(self, field_names, declared_fields, extra_kwargs):
"""
Returns all the model fields that are being mapped to by fields
on the serializer class.
Returned as a dict of 'model field name' -> 'model field'.
Used internally by `get_uniqueness_field_options`.
"""
model = getattr(self.Meta, 'model')
model_fields = {}
for field_name in field_names:
if field_name in declared_fields:
# Field is explicitly declared on the class, use that.
ret[field_name] = declared_fields[field_name]
# If the field is declared on the serializer
field = declared_fields[field_name]
source = field.source or field_name
else:
try:
source = extra_kwargs[field_name]['source']
except KeyError:
source = field_name
if '.' in source or source == '*':
# Model fields will always have a simple source mapping,
# they can't be nested attribute lookups.
continue
elif field_name in info.fields_and_pk:
# Create regular model fields.
model_field = info.fields_and_pk[field_name]
field_cls = self._field_mapping[model_field]
kwargs = get_field_kwargs(field_name, model_field)
if 'choices' in kwargs:
# Fields with choices get coerced into `ChoiceField`
# instead of using their regular typed field.
field_cls = ChoiceField
if not issubclass(field_cls, ModelField):
# `model_field` is only valid for the fallback case of
# `ModelField`, which is used when no other typed field
# matched to the model field.
kwargs.pop('model_field', None)
if not issubclass(field_cls, CharField) and not issubclass(field_cls, ChoiceField):
# `allow_blank` is only valid for textual fields.
kwargs.pop('allow_blank', None)
try:
field = model._meta.get_field(source)
if isinstance(field, DjangoModelField):
model_fields[source] = field
except FieldDoesNotExist:
pass
elif field_name in info.relations:
# Create forward and reverse relationships.
relation_info = info.relations[field_name]
if depth:
field_cls = self._get_nested_class(depth, relation_info)
kwargs = get_nested_relation_kwargs(relation_info)
else:
field_cls = self._related_class
kwargs = get_relation_kwargs(field_name, relation_info)
# `view_name` is only valid for hyperlinked relationships.
if not issubclass(field_cls, HyperlinkedRelatedField):
kwargs.pop('view_name', None)
return model_fields
elif hasattr(model, field_name):
# Create a read only field for model methods and properties.
field_cls = ReadOnlyField
kwargs = {}
# Determine the validators to apply...
elif field_name == api_settings.URL_FIELD_NAME:
# Create the URL field.
field_cls = HyperlinkedIdentityField
kwargs = get_url_kwargs(model)
def get_validators(self):
"""
Determine the set of validators to use when instantiating serializer.
"""
# If the validators have been declared explicitly then use that.
validators = getattr(getattr(self, 'Meta', None), 'validators', None)
if validators is not None:
return validators[:]
else:
raise ImproperlyConfigured(
'Field name `%s` is not valid for model `%s`.' %
(field_name, model.__class__.__name__)
)
# Populate any kwargs defined in `Meta.extra_kwargs`
extras = extra_kwargs.get(field_name, {})
if extras.get('read_only', False):
for attr in [
'required', 'default', 'allow_blank', 'allow_null',
'min_length', 'max_length', 'min_value', 'max_value',
'validators', 'queryset'
]:
kwargs.pop(attr, None)
if extras.get('default') and kwargs.get('required') is False:
kwargs.pop('required')
kwargs.update(extras)
# Create the serializer field.
ret[field_name] = field_cls(**kwargs)
for field_name, field in hidden_fields.items():
ret[field_name] = field
return ret
def _include_additional_options(self, extra_kwargs):
read_only_fields = getattr(self.Meta, 'read_only_fields', None)
if read_only_fields is not None:
for field_name in read_only_fields:
kwargs = extra_kwargs.get(field_name, {})
kwargs['read_only'] = True
extra_kwargs[field_name] = kwargs
# These are all pending deprecation.
write_only_fields = getattr(self.Meta, 'write_only_fields', None)
if write_only_fields is not None:
warnings.warn(
"The `Meta.write_only_fields` option is pending deprecation. "
"Use `Meta.extra_kwargs={<field_name>: {'write_only': True}}` instead.",
PendingDeprecationWarning,
stacklevel=3
)
for field_name in write_only_fields:
kwargs = extra_kwargs.get(field_name, {})
kwargs['write_only'] = True
extra_kwargs[field_name] = kwargs
view_name = getattr(self.Meta, 'view_name', None)
if view_name is not None:
warnings.warn(
"The `Meta.view_name` option is pending deprecation. "
"Use `Meta.extra_kwargs={'url': {'view_name': ...}}` instead.",
PendingDeprecationWarning,
stacklevel=3
)
kwargs = extra_kwargs.get(api_settings.URL_FIELD_NAME, {})
kwargs['view_name'] = view_name
extra_kwargs[api_settings.URL_FIELD_NAME] = kwargs
lookup_field = getattr(self.Meta, 'lookup_field', None)
if lookup_field is not None:
warnings.warn(
"The `Meta.lookup_field` option is pending deprecation. "
"Use `Meta.extra_kwargs={'url': {'lookup_field': ...}}` instead.",
PendingDeprecationWarning,
stacklevel=3
)
kwargs = extra_kwargs.get(api_settings.URL_FIELD_NAME, {})
kwargs['lookup_field'] = lookup_field
extra_kwargs[api_settings.URL_FIELD_NAME] = kwargs
return extra_kwargs
def _get_default_field_names(self, declared_fields, model_info):
# Otherwise use the default set of validators.
return (
[model_info.pk.name] +
list(declared_fields.keys()) +
list(model_info.fields.keys()) +
list(model_info.forward_relations.keys())
self.get_unique_together_validators() +
self.get_unique_for_date_validators()
)
def _get_nested_class(self, nested_depth, relation_info):
class NestedSerializer(ModelSerializer):
class Meta:
model = relation_info.related
depth = nested_depth - 1
def get_unique_together_validators(self):
"""
Determine a default set of validators for any unique_together contraints.
"""
model_class_inheritance_tree = (
[self.Meta.model] +
list(self.Meta.model._meta.parents.keys())
)
return NestedSerializer
# The field names we're passing though here only include fields
# which may map onto a model field. Any dotted field name lookups
# cannot map to a field, and must be a traversal, so we're not
# including those.
field_names = set([
field.source for field in self.fields.values()
if (field.source != '*') and ('.' not in field.source)
])
# Note that we make sure to check `unique_together` both on the
# base model class, but also on any parent classes.
validators = []
for parent_class in model_class_inheritance_tree:
for unique_together in parent_class._meta.unique_together:
if field_names.issuperset(set(unique_together)):
validator = UniqueTogetherValidator(
queryset=parent_class._default_manager,
fields=unique_together
)
validators.append(validator)
return validators
def get_unique_for_date_validators(self):
"""
Determine a default set of validators for the following contraints:
* unique_for_date
* unique_for_month
* unique_for_year
"""
info = model_meta.get_field_info(self.Meta.model)
default_manager = self.Meta.model._default_manager
field_names = [field.source for field in self.fields.values()]
validators = []
for field_name, field in info.fields_and_pk.items():
if field.unique_for_date and field_name in field_names:
validator = UniqueForDateValidator(
queryset=default_manager,
field=field_name,
date_field=field.unique_for_date
)
validators.append(validator)
if field.unique_for_month and field_name in field_names:
validator = UniqueForMonthValidator(
queryset=default_manager,
field=field_name,
date_field=field.unique_for_month
)
validators.append(validator)
if field.unique_for_year and field_name in field_names:
validator = UniqueForYearValidator(
queryset=default_manager,
field=field_name,
date_field=field.unique_for_year
)
validators.append(validator)
return validators
if hasattr(models, 'UUIDField'):
ModelSerializer._field_mapping[models.UUIDField] = UUIDField
ModelSerializer.serializer_field_mapping[models.UUIDField] = UUIDField
if postgres_fields:
class CharMappingField(DictField):
child = CharField()
ModelSerializer._field_mapping[postgres_fields.HStoreField] = CharMappingField
ModelSerializer.serializer_field_mapping[postgres_fields.HStoreField] = CharMappingField
ModelSerializer.serializer_field_mapping[postgres_fields.ArrayField] = ListField
class HyperlinkedModelSerializer(ModelSerializer):
@ -1152,9 +1360,13 @@ class HyperlinkedModelSerializer(ModelSerializer):
* A 'url' field is included instead of the 'id' field.
* Relationships to other instances are hyperlinks, instead of primary keys.
"""
_related_class = HyperlinkedRelatedField
serializer_related_field = HyperlinkedRelatedField
def _get_default_field_names(self, declared_fields, model_info):
def get_default_field_names(self, declared_fields, model_info):
"""
Return the default list of field names that will be used if the
`Meta.fields` option is not specified.
"""
return (
[api_settings.URL_FIELD_NAME] +
list(declared_fields.keys()) +
@ -1162,10 +1374,16 @@ class HyperlinkedModelSerializer(ModelSerializer):
list(model_info.forward_relations.keys())
)
def _get_nested_class(self, nested_depth, relation_info):
def build_nested_field(self, field_name, relation_info, nested_depth):
"""
Create nested fields for forward and reverse relationships.
"""
class NestedSerializer(HyperlinkedModelSerializer):
class Meta:
model = relation_info.related
model = relation_info.related_model
depth = nested_depth - 1
return NestedSerializer
field_class = NestedSerializer
field_kwargs = get_nested_relation_kwargs(relation_info)
return field_class, field_kwargs

View File

@ -5,11 +5,11 @@ For example your project's `settings.py` file might look like this:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.YAMLRenderer',
'rest_framework.renderers.TemplateHTMLRenderer',
)
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.YAMLParser',
'rest_framework.parsers.TemplateHTMLRenderer',
)
}
@ -47,9 +47,10 @@ DEFAULTS = {
'DEFAULT_THROTTLE_CLASSES': (),
'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',
'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
'DEFAULT_VERSIONING_CLASS': None,
# Generic view behavior
'DEFAULT_PAGINATION_SERIALIZER_CLASS': 'rest_framework.pagination.PaginationSerializer',
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'DEFAULT_FILTER_BACKENDS': (),
# Throttling
@ -68,6 +69,11 @@ DEFAULTS = {
'SEARCH_PARAM': 'search',
'ORDERING_PARAM': 'ordering',
# Versioning
'DEFAULT_VERSION': None,
'ALLOWED_VERSIONS': None,
'VERSION_PARAM': 'version',
# Authentication
'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
'UNAUTHENTICATED_TOKEN': None,
@ -124,7 +130,8 @@ IMPORT_STRINGS = (
'DEFAULT_THROTTLE_CLASSES',
'DEFAULT_CONTENT_NEGOTIATION_CLASS',
'DEFAULT_METADATA_CLASS',
'DEFAULT_PAGINATION_SERIALIZER_CLASS',
'DEFAULT_VERSIONING_CLASS',
'DEFAULT_PAGINATION_CLASS',
'DEFAULT_FILTER_BACKENDS',
'EXCEPTION_HANDLER',
'TEST_REQUEST_RENDERER_CLASSES',
@ -140,7 +147,9 @@ def perform_import(val, setting_name):
If the given setting is a string import notation,
then perform the necessary import or imports.
"""
if isinstance(val, six.string_types):
if val is None:
return None
elif isinstance(val, six.string_types):
return import_from_string(val, setting_name)
elif isinstance(val, (list, tuple)):
return [import_from_string(item, setting_name) for item in val]

View File

@ -60,6 +60,23 @@ a single block in the template.
color: #C20000;
}
.pagination>.disabled>a,
.pagination>.disabled>a:hover,
.pagination>.disabled>a:focus {
cursor: not-allowed;
pointer-events: none;
}
.pager>.disabled>a,
.pager>.disabled>a:hover,
.pager>.disabled>a:focus {
pointer-events: none;
}
.pager .next {
margin-left: 10px;
}
/*=== dabapps bootstrap styles ====*/
html {
@ -185,10 +202,6 @@ body a:hover {
color: #c20000;
}
#content a span {
text-decoration: underline;
}
.request-info {
clear:both;
}

View File

@ -119,9 +119,18 @@
<div class="page-header">
<h1>{{ name }}</h1>
</div>
<div style="float:left">
{% block description %}
{{ description }}
{% endblock %}
</div>
{% if paginator %}
<nav style="float: right">
{% get_pagination_html paginator %}
</nav>
{% endif %}
<div class="request-info" style="clear: both" >
<pre class="prettyprint"><b>{{ request.method }}</b> {{ request.get_full_path }}</pre>
</div>

View File

@ -0,0 +1,27 @@
<ul class="pagination" style="margin: 5px 0 10px 0">
{% if previous_url %}
<li><a href="{{ previous_url }}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>
{% else %}
<li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>
{% endif %}
{% for page_link in page_links %}
{% if page_link.is_break %}
<li class="disabled">
<a href="#"><span aria-hidden="true">&hellip;</span></a>
</li>
{% else %}
{% if page_link.is_active %}
<li class="active"><a href="{{ page_link.url }}">{{ page_link.number }}</a></li>
{% else %}
<li><a href="{{ page_link.url }}">{{ page_link.number }}</a></li>
{% endif %}
{% endif %}
{% endfor %}
{% if next_url %}
<li><a href="{{ next_url }}" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>
{% else %}
<li class="disabled"><a href="#" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>
{% endif %}
</ul>

View File

@ -0,0 +1,12 @@
<ul class="pager">
{% if previous_url %}
<li class="previous"><a href="{{ previous_url }}">&laquo; Previous</a></li>
{% else %}
<li class="previous disabled"><a href="#">&laquo; Previous</a></li>
{% endif %}
{% if next_url %}
<li class="next"><a href="{{ next_url }}">Next &raquo;</a></li>
{% else %}
<li class="next disabled"><a href="#">Next &raquo;</li>
{% endif %}
</ul>

View File

@ -1,36 +1,25 @@
from __future__ import unicode_literals, absolute_import
from django import template
from django.core.urlresolvers import reverse, NoReverseMatch
from django.http import QueryDict
from django.utils import six
from django.utils.six.moves.urllib import parse as urlparse
from django.utils.encoding import iri_to_uri, force_text
from django.utils.html import escape
from django.utils.safestring import SafeData, mark_safe
from django.utils.html import smart_urlquote
from rest_framework.renderers import HTMLFormRenderer
from rest_framework.utils.urls import replace_query_param
import re
register = template.Library()
def replace_query_param(url, key, val):
"""
Given a URL and a key/val pair, set or replace an item in the query
parameters of the URL, and return the new URL.
"""
(scheme, netloc, path, query, fragment) = urlparse.urlsplit(url)
query_dict = QueryDict(query).copy()
query_dict[key] = val
query = query_dict.urlencode()
return urlparse.urlunsplit((scheme, netloc, path, query, fragment))
# Regex for adding classes to html snippets
class_re = re.compile(r'(?<=class=["\'])(.*)(?=["\'])')
# And the template tags themselves...
@register.simple_tag
def get_pagination_html(pager):
return pager.to_html()
@register.simple_tag
def render_field(field, style=None):

View File

@ -6,11 +6,9 @@ from django.db.models.query import QuerySet
from django.utils import six, timezone
from django.utils.encoding import force_text
from django.utils.functional import Promise
from rest_framework.compat import OrderedDict, total_seconds
from rest_framework.utils.serializer_helpers import ReturnDict, ReturnList
from rest_framework.compat import total_seconds
import datetime
import decimal
import types
import json
import uuid
@ -61,65 +59,3 @@ class JSONEncoder(json.JSONEncoder):
elif hasattr(obj, '__iter__'):
return tuple(item for item in obj)
return super(JSONEncoder, self).default(obj)
try:
import yaml
except ImportError:
SafeDumper = None
else:
# Adapted from http://pyyaml.org/attachment/ticket/161/use_ordered_dict.py
class SafeDumper(yaml.SafeDumper):
"""
Handles decimals as strings.
Handles OrderedDicts as usual dicts, but preserves field order, rather
than the usual behaviour of sorting the keys.
"""
def represent_decimal(self, data):
return self.represent_scalar('tag:yaml.org,2002:str', six.text_type(data))
def represent_mapping(self, tag, mapping, flow_style=None):
value = []
node = yaml.MappingNode(tag, value, flow_style=flow_style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
best_style = True
if hasattr(mapping, 'items'):
mapping = list(mapping.items())
if not isinstance(mapping, OrderedDict):
mapping.sort()
for item_key, item_value in mapping:
node_key = self.represent_data(item_key)
node_value = self.represent_data(item_value)
if not (isinstance(node_key, yaml.ScalarNode) and not node_key.style):
best_style = False
if not (isinstance(node_value, yaml.ScalarNode) and not node_value.style):
best_style = False
value.append((node_key, node_value))
if flow_style is None:
if self.default_flow_style is not None:
node.flow_style = self.default_flow_style
else:
node.flow_style = best_style
return node
SafeDumper.add_representer(
decimal.Decimal,
SafeDumper.represent_decimal
)
SafeDumper.add_representer(
OrderedDict,
yaml.representer.SafeRepresenter.represent_dict
)
SafeDumper.add_representer(
ReturnDict,
yaml.representer.SafeRepresenter.represent_dict
)
SafeDumper.add_representer(
ReturnList,
yaml.representer.SafeRepresenter.represent_list
)
SafeDumper.add_representer(
types.GeneratorType,
yaml.representer.SafeRepresenter.represent_list
)

View File

@ -2,12 +2,10 @@
Utility functions to return a formatted name and description for a given view.
"""
from __future__ import unicode_literals
import re
from django.utils.html import escape
from django.utils.safestring import mark_safe
from rest_framework.compat import apply_markdown, force_text
import re
def remove_trailing_string(content, trailing):
@ -59,4 +57,5 @@ def markup_description(description):
description = apply_markdown(description)
else:
description = escape(description).replace('\n', '<br />')
description = '<p>' + description + '</p>'
return mark_safe(description)

View File

@ -24,7 +24,7 @@ FieldInfo = namedtuple('FieldResult', [
RelationInfo = namedtuple('RelationInfo', [
'model_field',
'related',
'related_model',
'to_many',
'has_through_model'
])
@ -98,7 +98,7 @@ def _get_forward_relationships(opts):
for field in [field for field in opts.fields if field.serialize and field.rel]:
forward_relations[field.name] = RelationInfo(
model_field=field,
related=_resolve_model(field.rel.to),
related_model=_resolve_model(field.rel.to),
to_many=False,
has_through_model=False
)
@ -107,7 +107,7 @@ def _get_forward_relationships(opts):
for field in [field for field in opts.many_to_many if field.serialize]:
forward_relations[field.name] = RelationInfo(
model_field=field,
related=_resolve_model(field.rel.to),
related_model=_resolve_model(field.rel.to),
to_many=True,
has_through_model=(
not field.rel.through._meta.auto_created
@ -131,7 +131,7 @@ def _get_reverse_relationships(opts):
related = getattr(relation, 'related_model', relation.model)
reverse_relations[accessor_name] = RelationInfo(
model_field=None,
related=related,
related_model=related,
to_many=relation.field.rel.multiple,
has_through_model=False
)
@ -142,7 +142,7 @@ def _get_reverse_relationships(opts):
related = getattr(relation, 'related_model', relation.model)
reverse_relations[accessor_name] = RelationInfo(
model_field=None,
related=related,
related_model=related,
to_many=True,
has_through_model=(
(getattr(relation.field.rel, 'through', None) is not None)

View File

@ -0,0 +1,25 @@
from django.utils.six.moves.urllib import parse as urlparse
def replace_query_param(url, key, val):
"""
Given a URL and a key/val pair, set or replace an item in the query
parameters of the URL, and return the new URL.
"""
(scheme, netloc, path, query, fragment) = urlparse.urlsplit(url)
query_dict = urlparse.parse_qs(query)
query_dict[key] = [val]
query = urlparse.urlencode(sorted(list(query_dict.items())), doseq=True)
return urlparse.urlunsplit((scheme, netloc, path, query, fragment))
def remove_query_param(url, key):
"""
Given a URL and a key/val pair, remove an item in the query
parameters of the URL, and return the new URL.
"""
(scheme, netloc, path, query, fragment) = urlparse.urlsplit(url)
query_dict = urlparse.parse_qs(query)
query_dict.pop(key, None)
query = urlparse.urlencode(sorted(list(query_dict.items())), doseq=True)
return urlparse.urlunsplit((scheme, netloc, path, query, fragment))

View File

@ -0,0 +1,177 @@
# coding: utf-8
from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _
from rest_framework import exceptions
from rest_framework.compat import unicode_http_header
from rest_framework.reverse import _reverse
from rest_framework.settings import api_settings
from rest_framework.templatetags.rest_framework import replace_query_param
from rest_framework.utils.mediatypes import _MediaType
import re
class BaseVersioning(object):
default_version = api_settings.DEFAULT_VERSION
allowed_versions = api_settings.ALLOWED_VERSIONS
version_param = api_settings.VERSION_PARAM
def determine_version(self, request, *args, **kwargs):
msg = '{cls}.determine_version() must be implemented.'
raise NotImplementedError(msg.format(
cls=self.__class__.__name__
))
def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
return _reverse(viewname, args, kwargs, request, format, **extra)
def is_allowed_version(self, version):
if not self.allowed_versions:
return True
return (version == self.default_version) or (version in self.allowed_versions)
class AcceptHeaderVersioning(BaseVersioning):
"""
GET /something/ HTTP/1.1
Host: example.com
Accept: application/json; version=1.0
"""
invalid_version_message = _('Invalid version in "Accept" header.')
def determine_version(self, request, *args, **kwargs):
media_type = _MediaType(request.accepted_media_type)
version = media_type.params.get(self.version_param, self.default_version)
version = unicode_http_header(version)
if not self.is_allowed_version(version):
raise exceptions.NotAcceptable(self.invalid_version_message)
return version
# We don't need to implement `reverse`, as the versioning is based
# on the `Accept` header, not on the request URL.
class URLPathVersioning(BaseVersioning):
"""
To the client this is the same style as `NamespaceVersioning`.
The difference is in the backend - this implementation uses
Django's URL keyword arguments to determine the version.
An example URL conf for two views that accept two different versions.
urlpatterns = [
url(r'^(?P<version>{v1,v2})/users/$', users_list, name='users-list'),
url(r'^(?P<version>{v1,v2})/users/(?P<pk>[0-9]+)/$', users_detail, name='users-detail')
]
GET /1.0/something/ HTTP/1.1
Host: example.com
Accept: application/json
"""
invalid_version_message = _('Invalid version in URL path.')
def determine_version(self, request, *args, **kwargs):
version = kwargs.get(self.version_param, self.default_version)
if not self.is_allowed_version(version):
raise exceptions.NotFound(self.invalid_version_message)
return version
def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
if request.version is not None:
kwargs = {} if (kwargs is None) else kwargs
kwargs[self.version_param] = request.version
return super(URLPathVersioning, self).reverse(
viewname, args, kwargs, request, format, **extra
)
class NamespaceVersioning(BaseVersioning):
"""
To the client this is the same style as `URLPathVersioning`.
The difference is in the backend - this implementation uses
Django's URL namespaces to determine the version.
An example URL conf that is namespaced into two seperate versions
# users/urls.py
urlpatterns = [
url(r'^/users/$', users_list, name='users-list'),
url(r'^/users/(?P<pk>[0-9]+)/$', users_detail, name='users-detail')
]
# urls.py
urlpatterns = [
url(r'^v1/', include('users.urls', namespace='v1')),
url(r'^v2/', include('users.urls', namespace='v2'))
]
GET /1.0/something/ HTTP/1.1
Host: example.com
Accept: application/json
"""
invalid_version_message = _('Invalid version in URL path.')
def determine_version(self, request, *args, **kwargs):
resolver_match = getattr(request, 'resolver_match', None)
if (resolver_match is None or not resolver_match.namespace):
return self.default_version
version = resolver_match.namespace
if not self.is_allowed_version(version):
raise exceptions.NotFound(self.invalid_version_message)
return version
def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
if request.version is not None:
viewname = self.get_versioned_viewname(viewname, request)
return super(NamespaceVersioning, self).reverse(
viewname, args, kwargs, request, format, **extra
)
def get_versioned_viewname(self, viewname, request):
return request.version + ':' + viewname
class HostNameVersioning(BaseVersioning):
"""
GET /something/ HTTP/1.1
Host: v1.example.com
Accept: application/json
"""
hostname_regex = re.compile(r'^([a-zA-Z0-9]+)\.[a-zA-Z0-9]+\.[a-zA-Z0-9]+$')
invalid_version_message = _('Invalid version in hostname.')
def determine_version(self, request, *args, **kwargs):
hostname, seperator, port = request.get_host().partition(':')
match = self.hostname_regex.match(hostname)
if not match:
return self.default_version
version = match.group(1)
if not self.is_allowed_version(version):
raise exceptions.NotFound(self.invalid_version_message)
return version
# We don't need to implement `reverse`, as the hostname will already be
# preserved as part of the REST framework `reverse` implementation.
class QueryParameterVersioning(BaseVersioning):
"""
GET /something/?version=0.1 HTTP/1.1
Host: example.com
Accept: application/json
"""
invalid_version_message = _('Invalid version in query parameter.')
def determine_version(self, request, *args, **kwargs):
version = request.query_params.get(self.version_param)
if not self.is_allowed_version(version):
raise exceptions.NotFound(self.invalid_version_message)
return version
def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
url = super(QueryParameterVersioning, self).reverse(
viewname, args, kwargs, request, format, **extra
)
if request.version is not None:
return replace_query_param(url, self.version_param, request.version)
return url

Some files were not shown because too many files have changed in this diff Show More