From b04bd8618ca956c2ceab36a8729cab822f0cff93 Mon Sep 17 00:00:00 2001 From: cobaltchang Date: Tue, 5 Jul 2016 22:36:52 +0800 Subject: [PATCH 001/175] Fix the error without specified encoding when compiling (#4246) Fix setup.py error on some platforms --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0fef70f1f..86870489f 100755 --- a/setup.py +++ b/setup.py @@ -4,6 +4,7 @@ import os import re import shutil import sys +from io import open from setuptools import setup @@ -16,7 +17,7 @@ except ImportError: print("warning: pypandoc module not found, could not convert Markdown to RST") def read_md(f): - return open(f, 'r').read() + return open(f, 'r', encoding='utf-8').read() def get_version(package): From b10de374765d4abd42fae354d7ed5e875a9b2127 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 5 Jul 2016 15:58:36 +0100 Subject: [PATCH 002/175] Funding text tweaks [ci skip] (#4247) --- README.md | 7 +++---- docs/index.md | 5 ++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fc9bd43a5..89e73de83 100644 --- a/README.md +++ b/README.md @@ -18,16 +18,15 @@ REST framework commercially we strongly encourage you to invest in its continued development by **[signing up for a paid plan][funding]**. The initial aim is to provide a single full-time position on REST framework. -Right now we're a little over 45% of the way towards achieving that. -*Every single sign-up makes a significant impact.* Taking out a -[basic tier sponsorship](https://fund.django-rest-framework.org/topics/funding/#corporate-plans) moves us about 1% closer to our funding target. +Right now we're a little over 50% of the way towards achieving that. +*Every single sign-up makes a significant impact.*

-*Many thanks to all our [awesome sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/) and [Sentry](https://getsentry.com/welcome/).* +*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/) and [Sentry](https://getsentry.com/welcome/).* --- diff --git a/docs/index.md b/docs/index.md index 072d80c66..6f4b0bf07 100644 --- a/docs/index.md +++ b/docs/index.md @@ -68,9 +68,8 @@ REST framework commercially we strongly encourage you to invest in its continued development by **[signing up for a paid plan][funding]**. The initial aim is to provide a single full-time position on REST framework. -Right now we're a little over 43% of the way towards achieving that. -*Every single sign-up makes a significant impact.* Taking out a -[basic tier sponsorship](https://fund.django-rest-framework.org/topics/funding/#corporate-plans) moves us about 1% closer to our funding target. +Right now we're a little over 50% of the way towards achieving that. +*Every single sign-up makes a significant impact.*
-*Many thanks to all our [awesome sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), and [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf).* +*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), and [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf).* --- @@ -243,6 +243,7 @@ General guides to using REST framework. * [3.1 Announcement][3.1-announcement] * [3.2 Announcement][3.2-announcement] * [3.3 Announcement][3.3-announcement] +* [3.4 Announcement][3.4-announcement] * [Kickstarter Announcement][kickstarter-announcement] * [Mozilla Grant][mozilla-grant] * [Funding][funding] @@ -368,6 +369,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [3.1-announcement]: topics/3.1-announcement.md [3.2-announcement]: topics/3.2-announcement.md [3.3-announcement]: topics/3.3-announcement.md +[3.4-announcement]: topics/3.4-announcement.md [kickstarter-announcement]: topics/kickstarter-announcement.md [mozilla-grant]: topics/mozilla-grant.md [funding]: topics/funding.md diff --git a/docs/topics/3.4-announcement.md b/docs/topics/3.4-announcement.md new file mode 100644 index 000000000..d3af44298 --- /dev/null +++ b/docs/topics/3.4-announcement.md @@ -0,0 +1,172 @@ + + +# Django REST framework 3.4 + +The 3.4 release is the first in a planned series that will be addressing schema +generation, hypermedia support, API clients, and finally realtime support. + +--- + +## Funding + +The 3.4 release has been made possible a recent [Mozilla grant][moss], and by our +[collaborative funding model][funding]. If you use REST framework commercially, and would +like to see this work continue, we strongly encourage you to invest in its +continued development by **[signing up for a paid plan][funding]**. + +The initial aim is to provide a single full-time position on REST framework. +Right now we're over 60% of the way towards achieving that. +*Every single sign-up makes a significant impact.* + + +
+ +*Many thanks to all our [awesome sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), and [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf).* + +--- + +## Schemas & client libraries + +REST framework 3.4 brings built-in support for generating API schemas. + +We provide this support by using [Core API][core-api], a Document Object Model +for describing APIs. + +Because Core API represents the API schema in an format-independent +manner, we're able to render the Core API `Document` object into many different +schema formats, by allowing the renderer class to determine how the internal +representation maps onto the external schema format. + +This approach should also open the door to a range of auto-generated API +documentation options in the future, by rendering the `Document` object into +HTML documentation pages. + +Alongside the built-in schema support, we're also now providing the following: + +* A [command line tool][command-line-client] for interacting with APIs. +* A [Python client library][client-library] for interacting with APIs. + +These API clients are dynamically driven, and able to interact with any API +that exposes a supported schema format. + +Dynamically driven clients allow you to interact with an API at an application +layer interface, rather than a network layer interface, while still providing +the benefits of RESTful Web API design. + +We're expecting to expand the range of languages that we provide client libraries +for over the coming months. + +--- + +Current support for schema formats is as follows: + +Name | Support | PyPI package +---------------------------------|-------------------------------------|-------------------------------- +[Core JSON][core-json] | Schema generation & client support. | Built-in support in `coreapi`. +[Swagger / OpenAPI][swagger] | Schema generation & client support. | The `openapi-codec` package. +[JSON Hyper-Schema][hyperschema] | Currrently client support only. | The `hyperschema-codec` package. +[API Blueprint][api-blueprint] | Not yet available. | Not yet available. + +--- + +You can read more about any of this new functionality in the following: + +* New tutorial section on [schemas & client libraries][tut-7]. +* Documentation page on [schema generation][schema-generation]. +* Topic page on [API clients][api-clients]. + +It is also worth noting that Marc Gibbons is currently working towards a 2.0 release of +the popular Django REST Swagger package, which will tie in with our new built-in support. + +--- + +## Supported versions + +The 3.4.0 release adds support for Django 1.10. + +The following versions of Python and Django are now supported: + +* Django versions 1.8, 1.9, and 1.10. +* Python versions 2.7, 3.2(\*), 3.3(\*), 3.4, 3.5. + +(\*) Note that Python 3.2 and 3.3 are not supported from Django 1.9 onwards. + +--- + +## Deprecations and changes + +The 3.4 release includes very limited deprecation or behavioral changes, and +should present a straightforward upgrade. + +#### Use fields or exclude on serializer classes. + +The following change in 3.3.0 is now escalated from "pending deprecation" to +"deprecated". Its usage will continue to function but will raise warnings: + +`ModelSerializer` and `HyperlinkedModelSerializer` should include either a `fields` +option, or an `exclude` option. The `fields = '__all__'` shortcut may be used +to explicitly include all fields. + +#### Microsecond precision when returning time or datetime + +Using the default JSON renderer and directly returning a `datetime` or `time` +instance will now render with microsecond precision (6 digits), rather than +millisecond precision (3 digits). This makes the output format consistent with the +default string output of `serializers.DateTimeField` and `serializers.TimeField`. + +This change *does not affect the default behavior when using serializers*, +which is to serialize `datetime` and `time` instances into strings with +microsecond precision. + +The serializer behavior can be modified if needed, using the `DATETIME_FORMAT` +and `TIME_FORMAT` settings. + +The renderer behavior can be modified by setting a custom `encoder_class` +attribute on a `JSONRenderer` subclass. + +--- + +## Other improvements + +This release includes further work from a huge number of [pull requests and issues][milestone]. + +Many thanks to all our contributors who've been involved in the release, either through raising issues, giving feedback, improving the documentation, or suggesting and implementing code changes. + +The full set of itemized release notes [are available here][release-notes]. + +[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors +[moss]: mozilla-grant.md +[funding]: funding.md +[core-api]: http://www.coreapi.org/ +[command-line-client]: api-clients#command-line-client +[client-library]: api-clients#python-client-library +[core-json]: http://www.coreapi.org/specification/encoding/#core-json-encoding +[swagger]: https://openapis.org/specification +[hyperschema]: http://json-schema.org/latest/json-schema-hypermedia.html +[api-blueprint]: https://apiblueprint.org/ +[tut-7]: ../../tutorial/7-schemas-and-client-libraries/ +[schema-generation]: ../../api-guide/schemas/ +[api-clients]: api-clients.md +[milestone]: https://github.com/tomchristie/django-rest-framework/milestone/35 +[release-notes]: release-notes#34 diff --git a/docs/topics/api-clients.md b/docs/topics/api-clients.md index 635fd8ae0..25b0c6145 100644 --- a/docs/topics/api-clients.md +++ b/docs/topics/api-clients.md @@ -59,7 +59,7 @@ exposes a supported schema format. To install the Core API command line client, use `pip`. Note that the command-line client is a separate package to the -python client library `coreapi`. Make sure to install `coreapi-cli`. +python client library. Make sure to install `coreapi-cli`. $ pip install coreapi-cli diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 201549b45..5bfdad09b 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,12 +40,94 @@ You can determine your currently installed version using `pip freeze`: ## 3.4.x series -### 3.4 +### 3.4.0 -**Unreleased** +**Date**: [14th July 2016][3.4.0-milestone] -* Dropped support for EOL Django 1.7 ([#3933][gh3933]) -* Fixed null foreign keys targeting UUIDField primary keys. ([#3936][gh3936]) +* Don't strip microseconds in JSON output. ([#4256][gh4256]) +* Two slightly different iso 8601 datetime serialization. ([#4255][gh4255]) +* Resolve incorrect inclusion of media type parameters. ([#4254][gh4254]) +* Response Content-Type potentially malformed. ([#4253][gh4253]) +* Fix setup.py error on some platforms. ([#4246][gh4246]) +* Move alternate formats in coreapi into separate packages. ([#4244][gh4244]) +* Add localize keyword argument to `DecimalField`. ([#4233][gh4233]) +* Fix issues with routers for custom list-route and detail-routes. ([#4229][gh4229]) +* Namespace versioning with nested namespaces. ([#4219][gh4219]) +* Robust uniqueness checks. ([#4217][gh4217]) +* Minor refactoring of `must_call_distinct`. ([#4215][gh4215]) +* Overridable offset cutoff in CursorPagination. ([#4212][gh4212]) +* Pass through strings as-in with date/time fields. ([#4196][gh4196]) +* Add test confirming that required=False is valid on a relational field. ([#4195][gh4195]) +* In LimitOffsetPagination `limit=0` should revert to default limit. ([#4194][gh4194]) +* Exclude read_only=True fields from unique_together validation & add docs. ([#4192][gh4192]) +* Handle bytestrings in JSON. ([#4191][gh4191]) +* JSONField(binary=True) represents using binary strings, which JSONRenderer does not support. ([#4187][gh4187]) +* JSONField(binary=True) represents using binary strings, which JSONRenderer does not support. ([#4185][gh4185]) +* More robust form rendering in the browsable API. ([#4181][gh4181]) +* Empty cases of `.validated_data` and `.errors` as lists not dicts for ListSerializer. ([#4180][gh4180]) +* Schemas & client libraries. ([#4179][gh4179]) +* Removed `AUTH_USER_MODEL` compat property. ([#4176][gh4176]) +* Clean up existing deprecation warnings. ([#4166][gh4166]) +* Django 1.10 support. ([#4158][gh4158]) +* Updated jQuery version to 1.12.4. ([#4157][gh4157]) +* More robust default behavior on OrderingFilter. ([#4156][gh4156]) +* description.py codes and tests removal. ([#4153][gh4153]) +* Wrap guardian.VERSION in tuple. ([#4149][gh4149]) +* Refine validator for fields with kwargs. ([#4146][gh4146]) +* Fix None values representation in childs of ListField, DictField. ([#4118][gh4118]) +* Resolve TimeField representation for midnight value. ([#4107][gh4107]) +* Set proper status code in AdminRenderer for the redirection after POST/DELETE requests. ([#4106][gh4106]) +* TimeField render returns None instead of 00:00:00. ([#4105][gh4105]) +* Fix incorrectly named zh-hans and zh-hant locale path. ([#4103][gh4103]) +* Prevent raising exception when limit is 0. ([#4098][gh4098]) +* TokenAuthentication: Allow custom keyword in the header. ([#4097][gh4097]) +* Handle incorrectly padded HTTP basic auth header. ([#4090][gh4090]) +* LimitOffset pagination crashes Browseable API when limit=0. ([#4079][gh4079]) +* Fixed DecimalField arbitrary precision support. ([#4075][gh4075]) +* Added support for custom CSRF cookie names. ([#4049][gh4049]) +* Fix regression introduced by #4035. ([#4041][gh4041]) +* No auth view failing permission should raise 403. ([#4040][gh4040]) +* Fix string_types / text_types confusion. ([#4025][gh4025]) +* Do not list related field choices in OPTIONS requests. ([#4021][gh4021]) +* Fix typo. ([#4008][gh4008]) +* Reorder initializing the view. ([#4006][gh4006]) +* Type error in DjangoObjectPermissionsFilter on Python 3.4. ([#4005][gh4005]) +* Fixed use of deprecated Query.aggregates. ([#4003][gh4003]) +* Fix blank lines around docstrings. ([#4002][gh4002]) +* Fixed admin pagination when limit is 0. ([#3990][gh3990]) +* OrderingFilter adjustements. ([#3983][gh3983]) +* Non-required serializer related fields. ([#3976][gh3976]) +* Using safer calling way of "@api_view" in tutorial. ([#3971][gh3971]) +* ListSerializer doesn't handle unique_together constraints. ([#3970][gh3970]) +* Add missing migration file. ([#3968][gh3968]) +* `OrderingFilter` should call `get_serializer_class()` to determine default fields. ([#3964][gh3964]) +* Remove old django checks from tests and compat. ([#3953][gh3953]) +* Support callable as the value of `initial` for any `serializer.Field`. ([#3943][gh3943]) +* Prevented unnecessary distinct() call in SearchFilter. ([#3938][gh3938]) +* Fix None UUID ForeignKey serialization. ([#3936][gh3936]) +* Drop EOL Django 1.7. ([#3933][gh3933]) +* Add missing space in serializer error message. ([#3926][gh3926]) +* Fixed _force_text_recursive typo. ([#3908][gh3908]) +* Attempt to address Django 2.0 deprecate warnings related to `field.rel`. ([#3906][gh3906]) +* Fix parsing multipart data using a nested serializer with list. ([#3820][gh3820]) +* Resolving APIs URL to different namespaces. ([#3816][gh3816]) +* Do not HTML-escape `help_text` in Browsable API forms. ([#3812][gh3812]) +* OPTIONS fetches and shows all possible foreign keys in choices field. ([#3751][gh3751]) +* Django 1.9 deprecation warnings ([#3729][gh3729]) +* Test case for #3598 ([#3710][gh3710]) +* Adding support for multiple values for search filter. ([#3541][gh3541]) +* Use get_serializer_class in ordering filter. ([#3487][gh3487]) +* Serializers with many=True should return empty list rather than empty dict. ([#3476][gh3476]) +* LimitOffsetPagination limit=0 fix. ([#3444][gh3444]) +* Enable Validators to defer string evaluation and handle new string format. ([#3438][gh3438]) +* Unique validator is executed and breaks if field is invalid. ([#3381][gh3381]) +* Do not ignore overridden View.get_view_name() in breadcrumbs. ([#3273][gh3273]) +* Retry form rendering when rendering with serializer fails. ([#3164][gh3164]) +* Unique constraint prevents nested serializers from updating. ([#2996][gh2996]) +* Uniqueness validators should not be run for excluded (read_only) fields. ([#2848][gh2848]) +* UniqueValidator raises exception for nested objects. ([#2403][gh2403]) +* `lookup_type` is deprecated in favor of `lookup_expr`. ([#4259][gh4259]) +--- ## 3.3.x series @@ -127,6 +209,8 @@ You can determine your currently installed version using `pip freeze`: * Removed support for Django 1.5 & 1.6. ([#3421][gh3421], [#3429][gh3429]) * Removed 'south' migrations. ([#3495][gh3495]) +--- + ## 3.2.x series ### 3.2.5 @@ -410,6 +494,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.3.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.3.1+Release%22 [3.3.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.3.2+Release%22 [3.3.3-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.3.3+Release%22 +[3.4.0-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.0+Release%22 [gh2013]: https://github.com/tomchristie/django-rest-framework/issues/2013 @@ -726,3 +811,88 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh3636]: https://github.com/tomchristie/django-rest-framework/issues/3636 [gh3605]: https://github.com/tomchristie/django-rest-framework/issues/3605 [gh3604]: https://github.com/tomchristie/django-rest-framework/issues/3604 + + +[gh2403]: https://github.com/tomchristie/django-rest-framework/issues/2403 +[gh2848]: https://github.com/tomchristie/django-rest-framework/issues/2848 +[gh2996]: https://github.com/tomchristie/django-rest-framework/issues/2996 +[gh3164]: https://github.com/tomchristie/django-rest-framework/issues/3164 +[gh3273]: https://github.com/tomchristie/django-rest-framework/issues/3273 +[gh3381]: https://github.com/tomchristie/django-rest-framework/issues/3381 +[gh3438]: https://github.com/tomchristie/django-rest-framework/issues/3438 +[gh3444]: https://github.com/tomchristie/django-rest-framework/issues/3444 +[gh3476]: https://github.com/tomchristie/django-rest-framework/issues/3476 +[gh3487]: https://github.com/tomchristie/django-rest-framework/issues/3487 +[gh3541]: https://github.com/tomchristie/django-rest-framework/issues/3541 +[gh3710]: https://github.com/tomchristie/django-rest-framework/issues/3710 +[gh3729]: https://github.com/tomchristie/django-rest-framework/issues/3729 +[gh3751]: https://github.com/tomchristie/django-rest-framework/issues/3751 +[gh3812]: https://github.com/tomchristie/django-rest-framework/issues/3812 +[gh3816]: https://github.com/tomchristie/django-rest-framework/issues/3816 +[gh3820]: https://github.com/tomchristie/django-rest-framework/issues/3820 +[gh3906]: https://github.com/tomchristie/django-rest-framework/issues/3906 +[gh3908]: https://github.com/tomchristie/django-rest-framework/issues/3908 +[gh3926]: https://github.com/tomchristie/django-rest-framework/issues/3926 +[gh3933]: https://github.com/tomchristie/django-rest-framework/issues/3933 +[gh3936]: https://github.com/tomchristie/django-rest-framework/issues/3936 +[gh3938]: https://github.com/tomchristie/django-rest-framework/issues/3938 +[gh3943]: https://github.com/tomchristie/django-rest-framework/issues/3943 +[gh3953]: https://github.com/tomchristie/django-rest-framework/issues/3953 +[gh3964]: https://github.com/tomchristie/django-rest-framework/issues/3964 +[gh3968]: https://github.com/tomchristie/django-rest-framework/issues/3968 +[gh3970]: https://github.com/tomchristie/django-rest-framework/issues/3970 +[gh3971]: https://github.com/tomchristie/django-rest-framework/issues/3971 +[gh3976]: https://github.com/tomchristie/django-rest-framework/issues/3976 +[gh3983]: https://github.com/tomchristie/django-rest-framework/issues/3983 +[gh3990]: https://github.com/tomchristie/django-rest-framework/issues/3990 +[gh4002]: https://github.com/tomchristie/django-rest-framework/issues/4002 +[gh4003]: https://github.com/tomchristie/django-rest-framework/issues/4003 +[gh4005]: https://github.com/tomchristie/django-rest-framework/issues/4005 +[gh4006]: https://github.com/tomchristie/django-rest-framework/issues/4006 +[gh4008]: https://github.com/tomchristie/django-rest-framework/issues/4008 +[gh4021]: https://github.com/tomchristie/django-rest-framework/issues/4021 +[gh4025]: https://github.com/tomchristie/django-rest-framework/issues/4025 +[gh4040]: https://github.com/tomchristie/django-rest-framework/issues/4040 +[gh4041]: https://github.com/tomchristie/django-rest-framework/issues/4041 +[gh4049]: https://github.com/tomchristie/django-rest-framework/issues/4049 +[gh4075]: https://github.com/tomchristie/django-rest-framework/issues/4075 +[gh4079]: https://github.com/tomchristie/django-rest-framework/issues/4079 +[gh4090]: https://github.com/tomchristie/django-rest-framework/issues/4090 +[gh4097]: https://github.com/tomchristie/django-rest-framework/issues/4097 +[gh4098]: https://github.com/tomchristie/django-rest-framework/issues/4098 +[gh4103]: https://github.com/tomchristie/django-rest-framework/issues/4103 +[gh4105]: https://github.com/tomchristie/django-rest-framework/issues/4105 +[gh4106]: https://github.com/tomchristie/django-rest-framework/issues/4106 +[gh4107]: https://github.com/tomchristie/django-rest-framework/issues/4107 +[gh4118]: https://github.com/tomchristie/django-rest-framework/issues/4118 +[gh4146]: https://github.com/tomchristie/django-rest-framework/issues/4146 +[gh4149]: https://github.com/tomchristie/django-rest-framework/issues/4149 +[gh4153]: https://github.com/tomchristie/django-rest-framework/issues/4153 +[gh4156]: https://github.com/tomchristie/django-rest-framework/issues/4156 +[gh4157]: https://github.com/tomchristie/django-rest-framework/issues/4157 +[gh4158]: https://github.com/tomchristie/django-rest-framework/issues/4158 +[gh4166]: https://github.com/tomchristie/django-rest-framework/issues/4166 +[gh4176]: https://github.com/tomchristie/django-rest-framework/issues/4176 +[gh4179]: https://github.com/tomchristie/django-rest-framework/issues/4179 +[gh4180]: https://github.com/tomchristie/django-rest-framework/issues/4180 +[gh4181]: https://github.com/tomchristie/django-rest-framework/issues/4181 +[gh4185]: https://github.com/tomchristie/django-rest-framework/issues/4185 +[gh4187]: https://github.com/tomchristie/django-rest-framework/issues/4187 +[gh4191]: https://github.com/tomchristie/django-rest-framework/issues/4191 +[gh4192]: https://github.com/tomchristie/django-rest-framework/issues/4192 +[gh4194]: https://github.com/tomchristie/django-rest-framework/issues/4194 +[gh4195]: https://github.com/tomchristie/django-rest-framework/issues/4195 +[gh4196]: https://github.com/tomchristie/django-rest-framework/issues/4196 +[gh4212]: https://github.com/tomchristie/django-rest-framework/issues/4212 +[gh4215]: https://github.com/tomchristie/django-rest-framework/issues/4215 +[gh4217]: https://github.com/tomchristie/django-rest-framework/issues/4217 +[gh4219]: https://github.com/tomchristie/django-rest-framework/issues/4219 +[gh4229]: https://github.com/tomchristie/django-rest-framework/issues/4229 +[gh4233]: https://github.com/tomchristie/django-rest-framework/issues/4233 +[gh4244]: https://github.com/tomchristie/django-rest-framework/issues/4244 +[gh4246]: https://github.com/tomchristie/django-rest-framework/issues/4246 +[gh4253]: https://github.com/tomchristie/django-rest-framework/issues/4253 +[gh4254]: https://github.com/tomchristie/django-rest-framework/issues/4254 +[gh4255]: https://github.com/tomchristie/django-rest-framework/issues/4255 +[gh4256]: https://github.com/tomchristie/django-rest-framework/issues/4256 +[gh4259]: https://github.com/tomchristie/django-rest-framework/issues/4259 diff --git a/mkdocs.yml b/mkdocs.yml index b10fbefb5..0b89988b1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -65,6 +65,7 @@ pages: - '3.1 Announcement': 'topics/3.1-announcement.md' - '3.2 Announcement': 'topics/3.2-announcement.md' - '3.3 Announcement': 'topics/3.3-announcement.md' + - '3.4 Announcement': 'topics/3.4-announcement.md' - 'Kickstarter Announcement': 'topics/kickstarter-announcement.md' - 'Mozilla Grant': 'topics/mozilla-grant.md' - 'Funding': 'topics/funding.md' diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 6f02b8039..ab1a16597 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ ______ _____ _____ _____ __ """ __title__ = 'Django REST framework' -__version__ = '3.3.3' +__version__ = '3.4.0' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2016 Tom Christie' diff --git a/rest_framework/locale/ach/LC_MESSAGES/django.mo b/rest_framework/locale/ach/LC_MESSAGES/django.mo index a91bc8b323901c1ec2e6b2cbbaba7c19085ab063..495c1ce7d7e83414ea9a9c8b538eb04f08d0bd4f 100644 GIT binary patch delta 90 zcmZowqv hGV*g1ixr$RiZY8!GE)_7O7e3ZfH)Z{u<;KoBLI_#9OwW5 delta 90 zcmZo4GCE}z8W65WuZ#Ju91#FG3XD}{)} ivdq*X1^=Ry{KC>o1)GX|2+JWSGcUg^GjZb|Rz?7t5*;c4 diff --git a/rest_framework/locale/ach/LC_MESSAGES/django.po b/rest_framework/locale/ach/LC_MESSAGES/django.po index bfb8b75cc..a245f1510 100644 --- a/rest_framework/locale/ach/LC_MESSAGES/django.po +++ b/rest_framework/locale/ach/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Acoli (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/ach/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: ach\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/ar/LC_MESSAGES/django.mo b/rest_framework/locale/ar/LC_MESSAGES/django.mo index 719bb0b318cd7893e394a3c335855cd27d5af81e..06471de238d2249bd794f744ebb7c4c710622b4f 100644 GIT binary patch delta 1608 zcmY+@e`u6-9KiAKcH7;4=$$*A=9T))oR-_UojY63)Vak7PqW$t$snHGle^mP;JMQn zXmhot3ECgu78WFd(sXgf?Oa4c5(EWNe-%AXLBZ&sLInR9L=g1;JkLYqm-{}i=lgxW zKR@5^U2gq->%_0|ni)k(kslymtx~EV+e17^kJc#Ff;-W`qj(>lLTNvP_v1V^;P;qB z7b6&{RjLnTIDpS!FTRW2N=>MrdD%rpJgig(pTr(~4{4xgWzG?~!A<+vMVf((yV z91QBmv6J#yjN)gQz^{YyBFf(G;uhvtOT4V6VqKlTgEaC}djp?9)}WpaJc6>2=Wz|5 z3d(PzM52rx_#Mj5|3UVsdh7jh2C$9tL9AhZHO|Xgd^PYA(nrnXcD#v+YNhVr7RvR@ z{3A-CLHPvA@x6l&;WyZfcZ2!};ga!o;w1LrTlf`DNXUnX(?0wFWyOnl7F%QfS(R`P zTr!Qaqs!QVv$zg_!ri!pvXQQp{)8v-H01@9{#~p51@Bo!{AJ<{ z6)aX=!mW56)A$3*8P_wr4A_EID9Iqp#oJ7l`y)G+#mEM>kvqw9?u}&D<3$MmOI$m- z|NhEla+!!DxhKhLCCP3jw5-Rw19AZ+B9hpYm*gl2pch)XuqkqqENPRE_uR;{Iw;Gf zR1cHaljU#GLY4?fl3U8(*h})C=lH!ON3ww|mrW-^2^|le2^&NCm#v(>5bB8Z+m>0h zhATHi9o5YPS!;CIcriO#wCsW!@YR3Rc81L3mX3yZ)fT;ud&BLaj5V%b3THMPvWG1@ zn>((aG7E)Q^7gPAv`qWhh#EZc!dSMbZ-f_@dDAKHSRcZqwa9QvUBE`rI#WX z8@iLJ?MCX6o>Z5};W! zwT#D#`LSN|v7n-IyRNso*(tlz&b;Bwx>L@aJL#Tt&pV&$w#Ibiiu;Ch!}(DE*0{EE zvT=WCGY!ksl$=>-&hJoiU!%j6`u`VwE4nS=EVz?2ylyz3GhW$n7Mv0<)acvMBhA5_ z_kPoTvFD<)4tJW)bIelG@5kOu^IB$U=bdZHeO4&T use4^JK#I{#3PpEaMAUUefme$zB8oz&zUO^c4;(+|8OQgW^E_vxYq_)dA>eu- zh%jR(qo+oQUQ9UnL;S=L{z4twoI*5X0@eCSY{NW8@Gc(1bsWY&n8ab15dFA>hw(X% z2vHQ9Oyc}-yw+Y&!T|X*R2#g;POPAT^;?8!#X;PTQ@9r|VG~O9;0mh$e~LTsT{W+u zIihA z@93SN*4|R!~{%5%L^FBV#v%iwTPGKd7AG8CZQ)E#jQkW2A6a zG*nfno~v7{a5HGT;QZFxQ13)lK!oL#t68R9H>9aG$#1S;*;jkOAt$t6d0#W+8!arq zYbWJ&-6uKbHe`vh=Dw4SMU1E(9f-#Ux{at|cutK+(wRbbKAW4344Dg=xZY<(4@C@n zUB9)?VBM%WzYsZ{H)rQ(%!OP&uAemv*-Ty^&tJ@4zH&`ZT+P|DC!VB3 tYTj5mVvYue?~6)&&i7aR{2rjc8wejj>(nav, 2015 msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Arabic (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/ar/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -18,74 +19,74 @@ msgstr "" "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:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "اسم المستخدم/كلمة السر غير صحيحين." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "المستخدم غير مفعل او تم حذفه." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." -msgstr "" +msgstr "رمز غير صحيح" #: authtoken/apps.py:7 msgid "Auth Token" -msgstr "" +msgstr "رمز التفويض" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" -msgstr "" +msgstr "المفتاح" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" -msgstr "" +msgstr "المستخدم" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" -msgstr "" +msgstr "أنشئ" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" -msgstr "" +msgstr "الرمز" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" -msgstr "" +msgstr "الرموز" #: authtoken/serializers.py:8 msgid "Username" -msgstr "" +msgstr "اسم المستخدم" #: authtoken/serializers.py:9 msgid "Password" -msgstr "" +msgstr "كلمة المرور" #: authtoken/serializers.py:20 msgid "User account is disabled." @@ -124,7 +125,6 @@ msgid "Not found." msgstr "غير موجود." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -133,7 +133,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -141,214 +140,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "هذا الحقل مطلوب." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "لا يمكن لهذا الحقل ان يكون فارغاً null." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" ليس قيمة منطقية." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "لا يمكن لهذا الحقل ان يكون فارغاً." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "تأكد ان الحقل لا يزيد عن {max_length} محرف." -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "تأكد ان الحقل {min_length} محرف على الاقل." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "عليك ان تدخل بريد إلكتروني صالح." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "هذه القيمة لا تطابق النمط المطلوب." -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "الرجاء إدخال رابط إلكتروني صالح." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "الرجاء إدخال رقم صحيح صالح." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "تأكد ان القيمة أقل أو تساوي {max_value}." -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "تأكد ان القيمة أكبر أو تساوي {min_value}." -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "الرجاء إدخال رقم صالح." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "تأكد ان القيمة لا تحوي أكثر من {max_digits} رقم." -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "صيغة التاريخ و الوقت غير صحيحة. عليك أن تستخدم واحدة من هذه الصيغ التالية: {format}." -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "صيغة التاريخ غير صحيحة. عليك أن تستخدم واحدة من هذه الصيغ التالية: {format}." -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "صيغة الوقت غير صحيحة. عليك أن تستخدم واحدة من هذه الصيغ التالية: {format}." -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" ليست واحدة من الخيارات الصالحة." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "لم يتم إرسال أي ملف." -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "الملف الذي تم إرساله فارغ." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "تأكد ان اسم الملف لا يحوي أكثر من {max_length} محرف (الإسم المرسل يحوي {length} محرف)." -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" +msgstr "أرسل" + +#: filters.py:336 +msgid "ascending" msgstr "" -#: pagination.py:189 +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." -msgstr "" +msgstr "صفحة غير صحيحة" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "معرف العنصر \"{pk_value}\" غير صالح - العنصر غير موجود." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -365,41 +351,38 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "قيمة غير صالحة." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" -msgstr "" +msgstr "مرشحات" #: templates/rest_framework/filters/django_filter.html:2 #: templates/rest_framework/filters/django_filter_crispyforms.html:4 msgid "Field filters" -msgstr "" +msgstr "مرشحات الحقول" #: templates/rest_framework/filters/ordering.html:3 msgid "Ordering" -msgstr "" +msgstr "الترتيب" #: templates/rest_framework/filters/search.html:2 msgid "Search" -msgstr "" +msgstr "البحث" #: templates/rest_framework/horizontal/radio.html:2 #: templates/rest_framework/inline/radio.html:2 @@ -413,27 +396,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -441,15 +420,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/be/LC_MESSAGES/django.mo b/rest_framework/locale/be/LC_MESSAGES/django.mo index cf959f6d9fb4bf89764a8ab67ec323c64e3cfbc5..13a9e256933805d001779e1ba01f975a592d26b5 100644 GIT binary patch delta 90 zcmeBY?Pr}Znaf<)&`811%*xPs;#@gL2+!2Y&_vq+2)KL_i%WDviW2jRa}rDPi>wqv hGV*g1ixr$RiZY8!GE)_7O7e3ZfH)Z{u<_4iMgX4>9h3k7 delta 90 zcmeBY?Pr}Znafz$z)-=^!phiU;#@gL2+thI(>4GCE}z8W65WuZ#Ju91#FG3XD}{)} ivdq*X1^=Ry{KC>o1)GX|2+JWSGcUg^GjZde$&3J{njO{v diff --git a/rest_framework/locale/be/LC_MESSAGES/django.po b/rest_framework/locale/be/LC_MESSAGES/django.po index 6fe59735f..5aaa072ae 100644 --- a/rest_framework/locale/be/LC_MESSAGES/django.po +++ b/rest_framework/locale/be/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Belarusian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/be/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: be\n" "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/ca/LC_MESSAGES/django.mo b/rest_framework/locale/ca/LC_MESSAGES/django.mo index e9d5355e1f5641ec5e75b8c3d454e0b0b9fd0858..7418c1ed05769800a93e799c05042bf4a73c7c7e 100644 GIT binary patch delta 75 zcmZ4Iz0P}sjwqM8uAz~Fp_!GT@nkbmIVjiE%FtwToT#`$NJf5cVzGjAMp0&QNoJ~o TO-X*P0}v-e1vXC=l@tH~A5IqS delta 75 zcmZ4Iz0P}sjwqM0u7RO~p@o&P#bh&4IVjg0#ElabSBOX~%S\n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Catalan (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/ca/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: ca\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "Header Basic invàlid. No hi ha disponibles les credencials." -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "Header Basic invàlid. Les credencials no poden contenir espais." -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "Header Basic invàlid. Les credencials no estan codificades correctament en base64." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Usuari/Contrasenya incorrectes." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "Usuari inactiu o esborrat." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "Token header invàlid. No s'han indicat les credencials." -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "Token header invàlid. El token no ha de contenir espais." -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "Token header invàlid. El token no pot contenir caràcters invàlids." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Token invàlid." @@ -58,23 +58,23 @@ msgstr "Token invàlid." msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "No trobat." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "Mètode \"{method}\" no permès." @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "No s'ha pogut satisfer l'Accept header de la petició." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "Media type \"{media_type}\" no suportat." @@ -140,214 +138,201 @@ msgstr "Media type \"{media_type}\" no suportat." msgid "Request was throttled." msgstr "La petició ha estat limitada pel número màxim de peticions definit." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "Aquest camp és obligatori." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Aquest camp no pot ser nul." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" no és un booleà." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "Aquest camp no pot estar en blanc." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "Aquest camp no pot tenir més de {max_length} caràcters." -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "Aquest camp ha de tenir un mínim de {min_length} caràcters." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Introdueixi una adreça de correu vàlida." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "Aquest valor no compleix el patró requerit." -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "Introdueix un \"slug\" vàlid consistent en lletres, números, guions o guions baixos." -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Introdueixi una URL vàlida." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "\"{value}\" no és un UUID vàlid." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "Introdueixi una adreça IPv4 o IPv6 vàlida." -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "Es requereix un nombre enter vàlid." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "Aquest valor ha de ser menor o igual a {max_value}." -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "Aquest valor ha de ser més gran o igual a {min_value}." -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "Valor del text massa gran." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "Es requereix un nombre vàlid." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "No pot haver-hi més de {max_digits} dígits en total." -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "No pot haver-hi més de {max_decimal_places} decimals." -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "No pot haver-hi més de {max_whole_digits} dígits abans del punt decimal." -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "El Datetime té un format incorrecte. Utilitzi un d'aquests formats: {format}." -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "S'espera un Datetime però s'ha rebut un Date." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "El Date té un format incorrecte. Utilitzi un d'aquests formats: {format}." -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "S'espera un Date però s'ha rebut un Datetime." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "El Time té un format incorrecte. Utilitzi un d'aquests formats: {format}." -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "La durada té un format incorrecte. Utilitzi un d'aquests formats: {format}." -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" no és una opció vàlida." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "S'espera una llista d'ítems però s'ha rebut el tipus \"{input_type}\"." -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "Aquesta selecció no pot estar buida." -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "\"{input}\" no és un path vàlid." -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "No s'ha enviat cap fitxer." -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "Les dades enviades no són un fitxer. Comproveu l'encoding type del formulari." -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "No s'ha pogut determinar el nom del fitxer." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "El fitxer enviat està buit." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "El nom del fitxer ha de tenir com a màxim {max_length} caràcters (en té {length})." -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "Envieu una imatge vàlida. El fitxer enviat no és una imatge o és una imatge corrompuda." -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "Aquesta llista no pot estar buida." -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "S'espera un diccionari però s'ha rebut el tipus \"{input_type}\"." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "Cursor invàlid." #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "PK invàlida \"{pk_value}\" - l'objecte no existeix." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "Tipus incorrecte. S'espera el valor d'una PK, s'ha rebut {data_type}." @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Hyperlink invàlid - L'objecte no existeix." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Tipus incorrecte. S'espera una URL, s'ha rebut {data_type}." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "L'objecte amb {slug_name}={value} no existeix." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Valor invàlid." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Dades invàlides. S'espera un diccionari però s'ha rebut {datatype}." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "Cap" msgid "No items to select." msgstr "Cap opció seleccionada." -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Aquest camp ha de ser únic." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "Aquests camps {field_names} han de constituir un conjunt únic." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "Aquest camp ha de ser únic per a la data \"{date_field}\"." -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "Aquest camp ha de ser únic per al mes \"{date_field}\"." -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "Aquest camp ha de ser únic per a l'any \"{date_field}\"." @@ -440,15 +418,19 @@ msgstr "Aquest camp ha de ser únic per a l'any \"{date_field}\"." msgid "Invalid version in \"Accept\" header." msgstr "Versió invàlida al header \"Accept\"." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "Versió invàlida a la URL." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Versió invàlida al hostname." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Versió invàlida al paràmetre de consulta." diff --git a/rest_framework/locale/ca_ES/LC_MESSAGES/django.mo b/rest_framework/locale/ca_ES/LC_MESSAGES/django.mo index 96a0b1ebb8a981b8d762b8c0e8319ee1e54c53a6..3a733aa1bff6f023acaa798f4d55f94a713ada8d 100644 GIT binary patch delta 90 zcmbQhGJ$2nWG-`ELn8%4Gb=;miF4%~Av{wnLlbQSAmH*zEH2RvDN4*M&PgoEFS1ey h$;i)5ELL#ND9S7@$xKzSDap@u0ODk*z{Wo!i~yb#9Tflo delta 90 zcmbQhGJ$2nWG-V}149Kv3oB!biF4%~Av|*+PulKWK|2o*h>J diff --git a/rest_framework/locale/ca_ES/LC_MESSAGES/django.po b/rest_framework/locale/ca_ES/LC_MESSAGES/django.po index c4f9df6cb..c9ce5fd13 100644 --- a/rest_framework/locale/ca_ES/LC_MESSAGES/django.po +++ b/rest_framework/locale/ca_ES/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Catalan (Spain) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/ca_ES/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: ca_ES\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/cs/LC_MESSAGES/django.mo b/rest_framework/locale/cs/LC_MESSAGES/django.mo index 459b3f01f444090391c6d45f9f44edd71036c949..1c98eb62d30fb3a73710923f9bd0428ea2b38668 100644 GIT binary patch delta 118 zcmX@^cHC`)hH$32uAz~Fp_!GTv9^JsfdN;5ziv=!S!Qu&ex9yNVo9o%f{}rtnXUmu zovD?fiM9a{aQP$_m*|ERCFT|9B$nhCSt*2M!EqWfqrYrYhK!mAfdN;5ziv=!S!Qu&ex9yNVo9o%f{}rtnXUmu zojF*Y0Z^S!VsVLXNKs;5aZX}Mevy?zL}FQHYLS9}QA&PcX{CZqMLvY(kdv90UzVA; Jd7|((egFVhC3ye< diff --git a/rest_framework/locale/cs/LC_MESSAGES/django.po b/rest_framework/locale/cs/LC_MESSAGES/django.po index 43c571130..8ba979350 100644 --- a/rest_framework/locale/cs/LC_MESSAGES/django.po +++ b/rest_framework/locale/cs/LC_MESSAGES/django.po @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Czech (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/cs/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -19,40 +19,40 @@ msgstr "" "Language: cs\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "Chybná hlavička. Nebyly poskytnuty přihlašovací údaje." -#: authentication.py:74 +#: authentication.py:76 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:80 +#: authentication.py:82 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:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Chybné uživatelské jméno nebo heslo." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "Uživatelský účet je neaktivní nebo byl smazán." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "Chybná hlavička tokenu. Nebyly zadány přihlašovací údaje." -#: authentication.py:176 +#: authentication.py:179 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:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Chybný token." @@ -60,23 +60,23 @@ msgstr "Chybný token." msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -125,7 +125,6 @@ msgid "Not found." msgstr "Nenalezeno." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "Metoda \"{method}\" není povolena." @@ -134,7 +133,6 @@ msgid "Could not satisfy the request Accept header." msgstr "Nelze vyhovět požadavku v hlavičce Accept." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "Nepodporovaný media type \"{media_type}\" v požadavku." @@ -142,214 +140,201 @@ msgstr "Nepodporovaný media type \"{media_type}\" v požadavku." msgid "Request was throttled." msgstr "Požadavek byl limitován kvůli omezení počtu požadavků za časovou periodu." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "Toto pole je vyžadováno." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Toto pole nesmí být prázdné (null)." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" nelze použít jako typ boolean." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "Toto pole nesmí být prázdné." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "Zkontrolujte, že toto pole není delší než {max_length} znaků." -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "Zkontrolujte, že toto pole obsahuje alespoň {min_length} znaků." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Vložte platnou e-mailovou adresu." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "Hodnota v tomto poli neodpovídá požadovanému formátu." -#: fields.py:730 +#: fields.py:735 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:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Vložte platný odkaz." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "\"{value}\" není platné UUID." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "Je vyžadováno celé číslo." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "Zkontrolujte, že hodnota je menší nebo rovna {max_value}." -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 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:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "Řetězec je příliš dlouhý." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "Je vyžadováno číslo." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 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:888 -#, python-brace-format +#: fields.py:894 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:889 -#, python-brace-format +#: fields.py:895 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:1004 -#, python-brace-format +#: fields.py:1025 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:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "Bylo zadáno pouze datum bez času." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 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:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "Bylo zadáno datum a čas, místo samotného data." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 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:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" není platnou možností." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 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:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "Nebyl zaslán žádný soubor." -#: fields.py:1348 +#: fields.py:1359 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:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "Nebylo možné zjistit jméno souboru." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "Zaslaný soubor je prázdný." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 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:1399 +#: fields.py:1410 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:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "Byl očekáván slovník položek ale nalezen \"{input_type}\"." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "Chybný kurzor." #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Chybný primární klíč \"{pk_value}\" - objekt neexistuje." #: relations.py:208 -#, python-brace-format 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." @@ -366,25 +351,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Chybný odkaz - objekt neexistuje." #: relations.py:243 -#, python-brace-format 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:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "Objekt s {slug_name}={value} neexistuje." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Chybná hodnota." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Chybná data. Byl přijat typ {datatype} místo očekávaného slovníku." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -414,27 +396,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Tato položka musí být unikátní." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "Položka {field_names} musí tvořit unikátní množinu." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 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:241 -#, python-brace-format +#: validators.py:260 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:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "Tato položka musí být pro rok \"{date_field}\" unikátní." @@ -442,15 +420,19 @@ msgstr "Tato položka musí být pro rok \"{date_field}\" unikátní." msgid "Invalid version in \"Accept\" header." msgstr "Chybné číslo verze v hlavičce Accept." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "Chybné číslo verze v odkazu." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Chybné číslo verze v hostname." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Chybné čislo verze v URL parametru." diff --git a/rest_framework/locale/da/LC_MESSAGES/django.mo b/rest_framework/locale/da/LC_MESSAGES/django.mo index 7820bdd3e93b02293df5e92d506df9ccfcbbc40e..9c17c3a405ef51c7a68fe18da1fd52289e1ffb74 100644 GIT binary patch delta 93 zcmbQ~JJWZAnHZP3uAz~Fp_!GT@ni=vIY$WB)XLCA+W-i-d=iUGbVG^~^NMp4OY)1X k6hboca}$ddoHL3ti%T+76>LiKa~*&<87i=OzSu8*0Ni{X`~Uy| delta 93 zcmbQ~JJWZAnHZO`u7RO~p@o&P#bgIDIY$WB9LUu+00J(b#Nra&kfOxA;+({i{30ub lh{UqY)FK7{qLlo?(n\n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Danish (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/da/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -19,40 +19,40 @@ msgstr "" "Language: da\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "Ugyldig basic header. Ingen legitimation angivet." -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "Ugyldig basic header. Legitimationsstrenge må ikke indeholde mellemrum." -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "Ugyldig basic header. Legitimationen er ikke base64 encoded på korrekt vis." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Ugyldigt brugernavn/kodeord." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "Inaktiv eller slettet bruger." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "Ugyldig token header." -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "Ugyldig token header. Token-strenge må ikke indeholde mellemrum." -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "Ugyldig token header. Token streng bør ikke indeholde ugyldige karakterer." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Ugyldigt token." @@ -60,23 +60,23 @@ msgstr "Ugyldigt token." msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -125,7 +125,6 @@ msgid "Not found." msgstr "Ikke fundet." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "Metoden \"{method}\" er ikke tilladt." @@ -134,7 +133,6 @@ msgid "Could not satisfy the request Accept header." msgstr "Kunne ikke efterkomme forespørgslens Accept header." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "Forespørgslens media type, \"{media_type}\", er ikke understøttet." @@ -142,214 +140,201 @@ msgstr "Forespørgslens media type, \"{media_type}\", er ikke understøttet." msgid "Request was throttled." msgstr "Forespørgslen blev neddroslet." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "Dette felt er påkrævet." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Dette felt må ikke være null." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" er ikke en tilladt boolsk værdi." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "Dette felt må ikke være tomt." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 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:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "Tjek at dette felt indeholder mindst {min_length} tegn." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Angiv en gyldig e-mailadresse." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "Denne værdi passer ikke med det påkrævede mønster." -#: fields.py:730 +#: fields.py:735 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:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Indtast en gyldig URL." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "\"{value}\" er ikke et gyldigt UUID." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "Indtast en gyldig IPv4 eller IPv6 adresse." -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "Et gyldigt heltal er påkrævet." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 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:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 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:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "Strengværdien er for stor." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "Et gyldigt tal er påkrævet." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 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:888 -#, python-brace-format +#: fields.py:894 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:889 -#, python-brace-format +#: fields.py:895 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:1004 -#, python-brace-format +#: fields.py:1025 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:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "Forventede en datotid, men fik en dato." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 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:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "Forventede en dato men fik en datotid." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 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:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "Varighed har forkert format. Brug istedet et af følgende formater: {format}." -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" er ikke et gyldigt valg." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "Flere end {count} objekter..." -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "Forventede en liste, men fik noget af typen \"{input_type}\"." -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "Dette valg kan være tomt." -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "\"{input}\" er ikke et gyldigt valg af adresse." -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "Ingen medsendt fil." -#: fields.py:1348 +#: fields.py:1359 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:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "Filnavnet kunne ikke afgøres." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "Den medsendte fil er tom." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 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:1399 +#: fields.py:1410 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:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "Denne liste er muligvis ikke tom." -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "Forventede en dictionary, men fik noget af typen \"{input_type}\"." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "Værdi skal være gyldig JSON." -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "Indsend." -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "Ugyldig cursor" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Ugyldig primærnøgle \"{pk_value}\" - objektet findes ikke." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "Ugyldig type. Forventet værdi er primærnøgle, fik {data_type}." @@ -366,25 +351,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Ugyldigt hyperlink - objektet findes ikke." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Forkert type. Forventede en URL-streng, fik {data_type}." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "Object med {slug_name}={value} findes ikke." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Ugyldig værdi." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Ugyldig data. Forventede en dictionary, men fik {datatype}." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "Filtre" @@ -414,27 +396,23 @@ msgstr "Ingen" msgid "No items to select." msgstr "Intet at vælge." -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Dette felt skal være unikt." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "Felterne {field_names} skal udgøre et unikt sæt." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "Dette felt skal være unikt for \"{date_field}\"-datoen." -#: validators.py:241 -#, python-brace-format +#: validators.py:260 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:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "Dette felt skal være unikt for \"{date_field}\"-året." @@ -442,15 +420,19 @@ msgstr "Dette felt skal være unikt for \"{date_field}\"-året." msgid "Invalid version in \"Accept\" header." msgstr "Ugyldig version i \"Accept\" headeren." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "Ugyldig version i URL-stien." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Ugyldig version i hostname." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Ugyldig version i forespørgselsparameteren." diff --git a/rest_framework/locale/da_DK/LC_MESSAGES/django.mo b/rest_framework/locale/da_DK/LC_MESSAGES/django.mo index 0e1cc36efce4bf67188b4092a8d23fca1d107dfb..f34583e4c20fbd84e69fe3c25847ea9bc43df310 100644 GIT binary patch delta 90 zcmbQpGLdD%WG-`ELn8%4Gb=;miF4%~Av{wnLlbQSAmH*zEH2RvDN4*M&PgoEFS1ey h$;i)5ELL#ND9S7@$xKzSDap@u0ODk*z{WqKi~yd<9T)%r delta 90 zcmbQpGLdD%WG-V}149Kv3oB!biF4%~Av|*+PulKWU34QXN_V diff --git a/rest_framework/locale/da_DK/LC_MESSAGES/django.po b/rest_framework/locale/da_DK/LC_MESSAGES/django.po index 7ec27dee2..bb512e284 100644 --- a/rest_framework/locale/da_DK/LC_MESSAGES/django.po +++ b/rest_framework/locale/da_DK/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Danish (Denmark) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/da_DK/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: da_DK\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/de/LC_MESSAGES/django.mo b/rest_framework/locale/de/LC_MESSAGES/django.mo index ce4458e0019ecbb7a953ea7b87b69c1cbad72051..317124886528d1de71f4021441d60acd700235dc 100644 GIT binary patch delta 2376 zcmZA2e@sL2RbwzW#nfVX4 zTHV>^Ki2$1rIl;me_Tq9^}|-nQEhEji@D0SVy!ltO`GfeIp^?4J>z>{&vQIKp6~N~ z&+(tGKX-?JNRK~lXntZDQ50v)2`pW}53SZ?Oaa!Rh5K+N9>Y95hdDTny8c^Sigz&? zGm?xc!#pg-4s6D8+-gkNTyr`mjNkHP&EBiZKoNIPSo+SdYJ={(YsF zo8x|5fp1|t-at+4mV5kxYhkMH7iJc8syN{VSimJ<1S=7&2iTW z0K7f=)Y6jfAzV*)lTb|&P*HH_P^5<~bHo<}8WvRMBmm_QmbAC+mHJ8s5x zjQjC=j4`jEZrDr77vTxi9$&;l{1Ua-ciiJy+*Ic^qE_@UGDkCt)%aqV&Q3a4@ernv zW;YID4^CqzW|MXwK7p#05AYF8V)+hXFSgx{Vt#oI-`D z(}wMM5_vDp3~I*N`OZ=nBCBRrqYoRg7LTJQ@*e7XkL7IL9%S3iDQv>CsFj?>wV2K_ zv2|hNqf^C!r%`);0d>P!)D6p6R|?fUiySf&?)YsiWBfkyY%}NToCH<5ZZlgO%?DO3@D zhgz{)sPpgQW{l;d(u+PU;Q8hZ9bI@;2k@rreQagC{9(rd+`@PgE&LsIej4d2flAa$ zcB5)&0uyi+7vRsh9_MfqT2yX$kj?=*%H%p~X=c#EIpk|;Qdc`Gl8f4+I*h@1LdB`< z=i7EVyd-whM~ttt{TXk-Qu`>^qeXBpKz7ys-*yM)&gb9GpovH&s6)H5W05U3U4-`CL+mHi^y=x; zx{c5ttLe2`MjX(9rVrIFyXWk;YD}%e!TxPiMYj;DcnZ}13g+N^OQfUMXTI&G)94Q3 zu$f3ABKH$>BVQ$c?6JB>pA3u`U!*g2N%S4h7Ej!czQhT)@m)+ax>TFs{2P)ZmpmEBdhffY*wq)AF-U9wbn1(dVik#WS!mbeVz00+~+yx ze9t*t?_2Cmd|Qxp+9*TB8sch(SpqLD;fr!H+pHAdLkF*51uo)REXy&w9V4jU58)m7 zFa|J*)%X$az-zb*L%FUAJ5HsI3m;-LW-l`vz;@h>XK*XdVFwm2Hw)u1>hCXOCw_(H zxb7CS0&GEbXaIG6!t+Jc^FF{j4fS^_d0Yta#h`2}YJ@GQ7sapv&*FCc23xRXg;@tu@GOyL>85Is4%}}KtKF1!sfg{+(+xzhX?!`<-(~X0;AK$>;=wozU*pZ+zLggjg ziGN`$HZy;EzzNg{ub^fouO!`(O58@f%kvrBLwg3by$Vasl9CtiUD=;y#R{Ix>UIvMqS+Yz_-uuR^7;!}B?8 zr9FoZuDLUPUn8pH!&t}r?I|i+G#9ZJ^B8_3M(_wGP$T~d9n4}yYNQpYDQ`s|{#*16 z6^&j4Ql8bZHiEvmX$1G33C;}nbkYSaG?fhEK0^8DaCAGPZAHG^qTVaVOn_VN_1un{ z+B`xj{eMx4=)c`VXa}fpVr%^AvJ`d^+Ab=CL^Cl&WD#V^JvKX#iE_^YcB{Mn;AYXT z;9rS*L~DzxXtuOSl@6s&g-N4$@6T}?|9!EYZx-b~LTEMX&`{|lv>jD+L~DU56=6ap zJB|B~W`KI1*VejH=}lu*%86EaCZQZ}A=(LT8MeGzmhioo&|=gfqT&#KqLk3ln|w5< zIN6u;S*k2|C@1yA%H_V)^9AQJlI^Q5C3F43DcJF(=&cB z{?ODT&bH(6bglVlY%D$7A8X=gV~3}rherebqtRnwr!94)=!P%#;OZZ9Q^VyE XU+Vn&`po3j4L>C3E9;WQRr~${XVK-< diff --git a/rest_framework/locale/de/LC_MESSAGES/django.po b/rest_framework/locale/de/LC_MESSAGES/django.po index c30dd7555..057a69f1c 100644 --- a/rest_framework/locale/de/LC_MESSAGES/django.po +++ b/rest_framework/locale/de/LC_MESSAGES/django.po @@ -5,7 +5,7 @@ # Translators: # Fabian Büchler , 2015 # Mads Jensen , 2015 -# Niklas P , 2015 +# Niklas P , 2015-2016 # Thomas Tanner, 2015 # Tom Jaster , 2015 # Xavier Ordoquy , 2015 @@ -13,9 +13,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: German (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -23,74 +23,74 @@ msgstr "" "Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "Ungültiger basic header. Keine Zugangsdaten angegeben." -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "Ungültiger basic header. Zugangsdaten sollen keine Leerzeichen enthalten." -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "Ungültiger basic header. Zugangsdaten sind nicht korrekt mit base64 kodiert." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Ungültiger Benutzername/Passwort" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "Benutzer inaktiv oder gelöscht." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "Ungültiger token header. Keine Zugangsdaten angegeben." -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "Ungültiger token header. Zugangsdaten sollen keine Leerzeichen enthalten." -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "Ungültiger Token Header. Tokens dürfen keine ungültigen Zeichen enthalten." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Ungültiges Token" #: authtoken/apps.py:7 msgid "Auth Token" -msgstr "" +msgstr "Auth Token" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" -msgstr "" +msgstr "Schlüssel" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" -msgstr "" +msgstr "Benutzer" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" -msgstr "" +msgstr "Token" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" -msgstr "" +msgstr "Tokens" #: authtoken/serializers.py:8 msgid "Username" -msgstr "" +msgstr "Benutzername" #: authtoken/serializers.py:9 msgid "Password" -msgstr "" +msgstr "Passwort" #: authtoken/serializers.py:20 msgid "User account is disabled." @@ -129,7 +129,6 @@ msgid "Not found." msgstr "Nicht gefunden." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "Methode \"{method}\" nicht erlaubt." @@ -138,7 +137,6 @@ msgid "Could not satisfy the request Accept header." msgstr "Kann die Accept Kopfzeile der Anfrage nicht erfüllen." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "Nicht unterstützter Medientyp \"{media_type}\" in der Anfrage." @@ -146,214 +144,201 @@ msgstr "Nicht unterstützter Medientyp \"{media_type}\" in der Anfrage." msgid "Request was throttled." msgstr "Die Anfrage wurde gedrosselt." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "Dieses Feld ist erforderlich." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Dieses Feld darf nicht Null sein." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" ist kein gültiger Wahrheitswert." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "Dieses Feld darf nicht leer sein." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 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:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "Stelle sicher, dass dieses Feld mindestens {min_length} Zeichen lang ist." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Gib eine gültige E-Mail Adresse an." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "Dieser Wert passt nicht zu dem erforderlichen Muster." -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "Gib ein gültiges \"slug\" aus Buchstaben, Ziffern, Unterstrichen und Minuszeichen ein." -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Gib eine gültige URL ein." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "\"{value}\" ist keine gültige UUID." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "Geben Sie eine gültige UPv4 oder IPv6 Adresse an" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "Eine gültige Ganzzahl ist erforderlich." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 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:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "Stelle sicher, dass dieser Wert größer oder gleich {min_value} ist." -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "Zeichenkette zu lang." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "Eine gültige Zahl ist erforderlich." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 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:888 -#, python-brace-format +#: fields.py:894 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:889 -#, python-brace-format +#: fields.py:895 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_digits} Stellen vor dem Komma lang ist." -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "Datums- und Zeitangabe hat das falsche Format. Nutze stattdessen eines dieser Formate: {format}." -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "Erwarte eine Datums- und Zeitangabe, erhielt aber ein Datum." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 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:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "Erwarte ein Datum, erhielt aber eine Datums- und Zeitangabe." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 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:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "Laufzeit hat das falsche Format. Benutze stattdessen eines dieser Formate {format}." -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" ist keine gültige Option." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "Mehr als {count} Ergebnisse" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 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:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "Diese Auswahl darf nicht leer sein" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "\"{input}\" ist ein ungültiger Pfad Wahl." -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "Es wurde keine Datei übermittelt." -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "Die übermittelten Daten stellen keine Datei dar. Prüfe den Kodierungstyp im Formular." -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "Der Dateiname konnte nicht ermittelt werden." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "Die übermittelte Datei ist leer." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "Stelle sicher, dass dieser Dateiname höchstens {max_length} Zeichen lang ist (er hat {length})." -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "Lade ein gültiges Bild hoch. Die hochgeladene Datei ist entweder kein Bild oder ein beschädigtes Bild." -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "Diese Liste darf nicht leer sein." -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "Erwarte ein Dictionary mit Elementen, erhielt aber den Typ \"{input_type}\"." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "Wert muss gültiges JSON sein." -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "Abschicken" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "Ungültiger Zeiger" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Ungültiger pk \"{pk_value}\" - Object existiert nicht." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "Falscher Typ. Erwarte pk Wert, erhielt aber {data_type}." @@ -370,25 +355,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Ungültiger Hyperlink - Objekt existiert nicht." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Falscher Typ. Erwarte URL Zeichenkette, erhielt aber {data_type}." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "Objekt mit {slug_name}={value} existiert nicht." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Ungültiger Wert." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Ungültige Daten. Dictionary erwartet, aber {datatype} erhalten." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "Filter" @@ -418,27 +400,23 @@ msgstr "Nichts" msgid "No items to select." msgstr "Keine Elemente zum Auswählen." -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Dieses Feld muss eindeutig sein." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "Die Felder {field_names} müssen eine eindeutige Menge bilden." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "Dieses Feld muss bezüglich des \"{date_field}\" Datums eindeutig sein." -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "Dieses Feld muss bezüglich des \"{date_field}\" Monats eindeutig sein." -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "Dieses Feld muss bezüglich des \"{date_field}\" Jahrs eindeutig sein." @@ -446,15 +424,19 @@ msgstr "Dieses Feld muss bezüglich des \"{date_field}\" Jahrs eindeutig sein." msgid "Invalid version in \"Accept\" header." msgstr "Ungültige Version in der \"Accept\" Kopfzeile." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "Ungültige Version im URL Pfad." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Ungültige Version im Hostname." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Ungültige Version im Anfrageparameter." diff --git a/rest_framework/locale/el/LC_MESSAGES/django.mo b/rest_framework/locale/el/LC_MESSAGES/django.mo index fc2120f7f371fbb381747daa830f56dca521fb92..c7fb97b2ca50c64f7f8d5b0a30b4d462f148575b 100644 GIT binary patch literal 13507 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 delta 151 zcmX?{*}!6PPl#nI0}wC*u?!Ha05LNV>i{tbSOD=cprj>`2C0F8$#=w}xr}uU3>6G5 ztc)!tJBrIWLb&EYuC@UXaQP$_m*|ERCFT|9B$nhCSt&#$mSv_EDfkzq, 2016 msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Greek (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/el/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,441 +18,423 @@ msgstr "" "Language: el\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." -msgstr "" +msgstr "Λανθασμένη επικεφαλίδα basic. Δεν υπάρχουν διαπιστευτήρια." -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." -msgstr "" +msgstr "Λανθασμένη επικεφαλίδα basic. Τα διαπιστευτήρια δε μπορεί να περιέχουν κενά." -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." -msgstr "" +msgstr "Λανθασμένη επικεφαλίδα basic. Τα διαπιστευτήρια δεν είναι κωδικοποιημένα κατά base64." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." -msgstr "" +msgstr "Λανθασμένο όνομα χρήστη/κωδικός." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." -msgstr "" - -#: authentication.py:173 -msgid "Invalid token header. No credentials provided." -msgstr "" +msgstr "Ο χρήστης είναι ανενεργός ή διεγραμμένος." #: authentication.py:176 -msgid "Invalid token header. Token string should not contain spaces." -msgstr "" +msgid "Invalid token header. No credentials provided." +msgstr "Λανθασμένη επικεφαλίδα token. Δεν υπάρχουν διαπιστευτήρια." -#: authentication.py:182 +#: authentication.py:179 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "Λανθασμένη επικεφαλίδα token. Το token δε πρέπει να περιέχει κενά." + +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." -msgstr "" +msgstr "Λανθασμένη επικεφαλίδα token. Το token περιέχει μη επιτρεπτούς χαρακτήρες." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." -msgstr "" +msgstr "Λανθασμένο token" #: authtoken/apps.py:7 msgid "Auth Token" -msgstr "" +msgstr "Token πιστοποίησης" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" -msgstr "" +msgstr "Κλειδί" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" -msgstr "" +msgstr "Χρήστης" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" -msgstr "" +msgstr "Δημιουργήθηκε" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" -msgstr "" +msgstr "Token" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" -msgstr "" +msgstr "Tokens" #: authtoken/serializers.py:8 msgid "Username" -msgstr "" +msgstr "Όνομα χρήστη" #: authtoken/serializers.py:9 msgid "Password" -msgstr "" +msgstr "Κωδικός" #: authtoken/serializers.py:20 msgid "User account is disabled." -msgstr "" +msgstr "Ο λογαριασμός χρήστη είναι απενεργοποιημένος." #: authtoken/serializers.py:23 msgid "Unable to log in with provided credentials." -msgstr "" +msgstr "Δεν είναι δυνατή η σύνδεση με τα διαπιστευτήρια." #: authtoken/serializers.py:26 msgid "Must include \"username\" and \"password\"." -msgstr "" +msgstr "Πρέπει να περιέχει \"όνομα χρήστη\" και \"κωδικό\"." #: exceptions.py:49 msgid "A server error occurred." -msgstr "" +msgstr "Σφάλμα διακομιστή." #: exceptions.py:84 msgid "Malformed request." -msgstr "" +msgstr "Λανθασμένο αίτημα." #: exceptions.py:89 msgid "Incorrect authentication credentials." -msgstr "" +msgstr "Λάθος διαπιστευτήρια." #: exceptions.py:94 msgid "Authentication credentials were not provided." -msgstr "" +msgstr "Δεν δόθηκαν διαπιστευτήρια." #: exceptions.py:99 msgid "You do not have permission to perform this action." -msgstr "" +msgstr "Δεν έχετε δικαίωματα για αυτή την ενέργεια." #: exceptions.py:104 views.py:81 msgid "Not found." -msgstr "" +msgstr "Δε βρέθηκε." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." -msgstr "" +msgstr "Η μέθοδος \"{method\"} δεν επιτρέπεται." #: exceptions.py:120 msgid "Could not satisfy the request Accept header." -msgstr "" +msgstr "Δεν ήταν δυνατή η ικανοποίηση της επικεφαλίδας Accept της αίτησης." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." -msgstr "" +msgstr "Δεν υποστηρίζεται το media type \"{media_type}\" της αίτησης." #: exceptions.py:145 msgid "Request was throttled." -msgstr "" +msgstr "Το αίτημα έγινε throttle." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." -msgstr "" +msgstr "Το πεδίο είναι απαραίτητο." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." -msgstr "" +msgstr "Το πεδίο δε μπορεί να είναι null." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." -msgstr "" +msgstr "Το \"{input}\" δεν είναι έγκυρο boolean." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." -msgstr "" +msgstr "Το πεδίο δε μπορεί να είναι κενό." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." -msgstr "" +msgstr "Επιβεβαιώσατε ότι το πεδίο δεν έχει περισσότερους από {max_length} χαρακτήρες." -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." -msgstr "" +msgstr "Επιβεβαιώσατε ότι το πεδίο έχει τουλάχιστον {min_length} χαρακτήρες." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." -msgstr "" +msgstr "Συμπληρώσατε μια έγκυρη διεύθυνση e-mail." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." -msgstr "" +msgstr "Η τιμή δε ταιριάζει με το pattern." -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." -msgstr "" +msgstr "Εισάγετε ένα έγκυρο \"slug\" που αποτελείται από γράμματα, αριθμούς παύλες και κάτω παύλες." -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." -msgstr "" +msgstr "Εισάγετε έγκυρο URL." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." -msgstr "" +msgstr "Το \"{value}\" δεν είναι έγκυρο UUID." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." -msgstr "" +msgstr "Εισάγετε μια έγκυρη διεύθυνση IPv4 ή IPv6." -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." -msgstr "" +msgstr "Ένας έγκυρος ακέραιος είναι απαραίτητος." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." -msgstr "" +msgstr "Επιβεβαιώσατε ότι η τιμή είναι μικρότερη ή ίση του {max_value}." -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." -msgstr "" +msgstr "Επιβεβαιώσατε ότι η τιμή είναι μεγαλύτερη ή ίση του {min_value}." -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." -msgstr "" +msgstr "Το κείμενο είναι πολύ μεγάλο." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." -msgstr "" +msgstr "Ένας έγκυρος αριθμός είναι απαραίτητος." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." -msgstr "" +msgstr "Επιβεβαιώσατε ότι δεν υπάρχουν παραπάνω από {max_digits} ψηφία." -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." -msgstr "" +msgstr "Επιβεβαιώσατε ότι δεν υπάρχουν παραπάνω από {max_decimal_places} δεκαδικά ψηφία." -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." -msgstr "" +msgstr "Επιβεβαιώσατε ότι δεν υπάρχουν παραπάνω από {max_whole_digits} ακέραια ψηφία." -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." -msgstr "" +msgstr "Η ημερομηνία έχεi λάθος μορφή. Χρησιμοποιήστε μια από τις ακόλουθες μορφές: {format}" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." -msgstr "" +msgstr "Αναμένεται ημερομηνία και ώρα αλλά δόθηκε μόνο ημερομηνία." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." -msgstr "" +msgstr "Η ημερομηνία έχεi λάθος μορφή. Χρησιμοποιήστε μια από τις ακόλουθες μορφές: {format}" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." -msgstr "" +msgstr "Αναμένεται ημερομηνία αλλά δόθηκε ημερομηνία και ώρα." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." -msgstr "" +msgstr "Η ώρα έχει λάθος μορφή. Χρησιμοποιήστε μια από τις ακόλουθες μορφές: {format}" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." -msgstr "" +msgstr "Η διάρκεια έχει λάθος μορφή. Χρησιμοποιήστε μια από τις ακόλουθες μορφές: {format}" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." -msgstr "" +msgstr "Το \"{input}\" δεν είναι έγκυρη επιλογή." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." -msgstr "" +msgstr "Περισσότερα από {count} αντικείμενα..." -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." -msgstr "" +msgstr "Αναμένεται μια λίστα αντικειμένον αλλά δόθηκε ο τύπος \"{input_type}\"" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." -msgstr "" +msgstr "Η επιλογή δε μπορεί να είναι κενή." -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." -msgstr "" +msgstr "Το \"{input}\" δεν είναι έγκυρη επιλογή διαδρομής." -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." -msgstr "" +msgstr "Δεν υποβλήθηκε αρχείο." -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." -msgstr "" +msgstr "Τα δεδομένα που υποβλήθηκαν δεν ήταν αρχείο. Ελέγξατε την κωδικοποίηση της φόρμας." -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." -msgstr "" +msgstr "Δε βρέθηκε όνομα αρχείου." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." -msgstr "" +msgstr "Το αρχείο που υποβλήθηκε είναι κενό." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." -msgstr "" +msgstr "Επιβεβαιώσατε ότι το όνομα αρχείου έχει ως {max_length} χαρακτήρες (έχει {length})." -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." -msgstr "" +msgstr "Ανεβάστε μια έγκυρη εικόνα. Το αρχείο που ανεβάσατε είτε δεν είναι εικόνα είτε έχει καταστραφεί." -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." -msgstr "" +msgstr "Η λίστα δε μπορεί να είναι κενή." -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." -msgstr "" +msgstr "Αναμένεται ένα λεξικό αντικείμενων αλλά δόθηκε ο τύπος \"{input_type}\"." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." -msgstr "" +msgstr "Η τιμή πρέπει να είναι μορφής JSON." -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" +msgstr "Υποβολή" + +#: filters.py:336 +msgid "ascending" msgstr "" -#: pagination.py:189 +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." -msgstr "" +msgstr "Λάθος σελίδα." -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" -msgstr "" +msgstr "Λάθος cursor." #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." -msgstr "" +msgstr "Λάθος κλειδί \"{pk_value}\" - το αντικείμενο δεν υπάρχει." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." -msgstr "" +msgstr "Λάθος τύπος. Αναμένεται τιμή κλειδιού, δόθηκε {data_type}." #: relations.py:240 msgid "Invalid hyperlink - No URL match." -msgstr "" +msgstr "Λάθος σύνδεση - δε ταιριάζει κάποιο URL." #: relations.py:241 msgid "Invalid hyperlink - Incorrect URL match." -msgstr "" +msgstr "Λάθος σύνδεση - δε ταιριάζει κάποιο URL." #: relations.py:242 msgid "Invalid hyperlink - Object does not exist." -msgstr "" +msgstr "Λάθος σύνδεση - το αντικείμενο δεν υπάρχει." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." -msgstr "" +msgstr "Λάθος τύπος. Αναμένεται URL, δόθηκε {data_type}." + +#: relations.py:401 +msgid "Object with {slug_name}={value} does not exist." +msgstr "Το αντικείμενο {slug_name}={value} δεν υπάρχει." #: relations.py:402 -#, python-brace-format -msgid "Object with {slug_name}={value} does not exist." -msgstr "" - -#: relations.py:403 msgid "Invalid value." -msgstr "" +msgstr "Λάθος τιμή." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." -msgstr "" +msgstr "Λάθος δεδομένα. Αναμένεται λεξικό αλλά δόθηκε {datatype}." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" -msgstr "" +msgstr "Φίλτρα" #: templates/rest_framework/filters/django_filter.html:2 #: templates/rest_framework/filters/django_filter_crispyforms.html:4 msgid "Field filters" -msgstr "" +msgstr "Φίλτρα πεδίων" #: templates/rest_framework/filters/ordering.html:3 msgid "Ordering" -msgstr "" +msgstr "Ταξινόμηση" #: templates/rest_framework/filters/search.html:2 msgid "Search" -msgstr "" +msgstr "Αναζήτηση" #: templates/rest_framework/horizontal/radio.html:2 #: templates/rest_framework/inline/radio.html:2 #: templates/rest_framework/vertical/radio.html:2 msgid "None" -msgstr "" +msgstr "None" #: templates/rest_framework/horizontal/select_multiple.html:2 #: templates/rest_framework/inline/select_multiple.html:2 #: templates/rest_framework/vertical/select_multiple.html:2 msgid "No items to select." -msgstr "" +msgstr "Δεν υπάρχουν αντικείμενα προς επιλογή." -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." -msgstr "" +msgstr "Το πεδίο πρέπει να είναι μοναδικό" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." -msgstr "" +msgstr "Τα πεδία {field_names} πρέπει να αποτελούν ένα μοναδικό σύνολο." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." -msgstr "" +msgstr "Το πεδίο πρέπει να είναι μοναδικό για την ημερομηνία \"{date_field}\"." -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." -msgstr "" +msgstr "Το πεδίο πρέπει να είναι μοναδικό για το μήνα \"{date_field}\"." -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." -msgstr "" +msgstr "Το πεδίο πρέπει να είναι μοναδικό για το έτος \"{date_field}\"." #: versioning.py:42 msgid "Invalid version in \"Accept\" header." -msgstr "" +msgstr "Λάθος έκδοση στην επικεφαλίδα \"Accept\"." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." +msgstr "Λάθος έκδοση στη διαδρομή URL." + +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." msgstr "" -#: versioning.py:144 +#: versioning.py:147 msgid "Invalid version in hostname." -msgstr "" +msgstr "Λάθος έκδοση στο hostname." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." -msgstr "" +msgstr "Λάθος έκδοση στην παράμετρο" #: views.py:88 msgid "Permission denied." -msgstr "" +msgstr "Απόρριψη πρόσβασης" diff --git a/rest_framework/locale/el_GR/LC_MESSAGES/django.mo b/rest_framework/locale/el_GR/LC_MESSAGES/django.mo index 67022a7f764708dd90e4ebb5696030a81fb70082..67018c3f97c474c10811d60112af016f806d3b22 100644 GIT binary patch delta 90 zcmeBY>1UZRnaf<)&`811%*xPs;#@gL2+!2Y&_vq+2)KL_i%WDviW2jRa}rDPi>wqv hGV*g1ixr$RiZY8!GE)_7O7e3ZfH)Z{u1UZRnafz$z)-=^!phiU;#@gL2+thI(>4GCE}z8W65WuZ#Ju91#FG3XD}{)} ivdq*X1^=Ry{KC>o1)GX|2+JWSGcUg^GjZb|VMYL>>K#-7 diff --git a/rest_framework/locale/el_GR/LC_MESSAGES/django.po b/rest_framework/locale/el_GR/LC_MESSAGES/django.po index 645a1a8c6..051a88783 100644 --- a/rest_framework/locale/el_GR/LC_MESSAGES/django.po +++ b/rest_framework/locale/el_GR/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Greek (Greece) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/el_GR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: el_GR\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/en/LC_MESSAGES/django.mo b/rest_framework/locale/en/LC_MESSAGES/django.mo index 06dc754a8d9fca02a5d8c0e2c534ad0ba349e006..13760f707539fa5c2f961f3edb61ce0ef08c3b1b 100644 GIT binary patch delta 2351 zcmbW&TWl0n9LMp0?-vwlOK;S|fI_*n$RcfZORrm7E4AfPiWIOc?b0@}yVBhd4aRv= zY)GhKMH7S+6E6uqR%`IVL`c+#h|y>;D%ymEkQfsQ`hb1#`=djJpwVQN0I8n{jGLD`2A+})p0<(S$VLhJ02K*Yg;KIAif;fQs z{3LecWxNlU+-+8fO{f>#hB_bhbj?oC(G6#@7H=Vcwt8XeP3uq@H=!Ohj1S>7Hse(c zVc9)q>v1n~%8sF~JB6$98`MnB<62z0i0ANp>!Py(ccE_Z5_aJS*obKbsT(%q62?9L zcnp=vGpNi?BU5VU{qem2c{%B4aJ~^W6RjMybbaXRhH*MocpOV`79G5dMfe+LVmbp> z#d7cgEJVt{+I+Vlms%g{J|n0ydLB#h1%G@7mGGNItiRU!TTUp0%EhS*gQ)R#R58V{ z4v(SM_<}#agGwM!oVuD#>$!=e&rvmW2sQPup?1xzOGlYrL1p?Y z>PESwRf9!XjjgC{6Gdfo7`0o@`|8V@$xdv+DeT9uupX;;NCOU{cFpS;ME3?AU9h4u zwQUAaMfDD9EiWOL*fszBU%tyoLpKazExM>(^FHcDFQPKOgnH2LsNGP#EVXMQ$gXki zEjp_Do2YG5=AVmg113yR2#Ft1CHix>QE~PELwRl4B*)NUw5jn|?w4xrZh0BUK@A@ydT`s1IGWw&{( z!!p)EOWcbZA4Mhb5xTnYdpZvOfqKJ|RsM`%7UN0O3rwN5&8w(ga{+hbZOp_Tri1#m z9lqnJOpp5Gmr=Xs9BSA6R7KvJnj4%*!(Bu$xidTDZlF*~x3B9)FPEA$84}n)j9Z1$*YvnJmsUd zCWY<8t$tr~67u^FYA*K>>BK=obKRdjlv7mROiweXwus<&%)-QQ@=Q*J8=0 zF1BJ7c48$S!W=w{>&#Mijz$v$Q_ck6YNr1l#_=k4Vq}Kd3fzSacoJLjCe~xcqrv-q zxPktsxB!30Fy`k43!IM{U*nrH>!qOyk6qA+UbNCsez4T|nr5q^d`$?vcN?_nO#x8j**)mVm_pbMYHAzY0=p(ZSN%&@%tOszh?=Jym5gzW z@_cJ{13jpnzKV+Ran#BuQP*h7^=Ca9BvA~PGrkcO+e5BDj@rPVNE%o^@$s+%wZK+f zhX*j3O=E(FR&)_{J-$UI=huTEdDs@yU3O?7@PnKtpMf)Ye zYX`NLA2dGzI#ZRl0RQZ?e3kM5+lK9~ts~Gmsd!YKT{ra+s*29`#q^1sNbwq4T^gdy zr1CRluTXcSzt1UAi2X!)8Tn`A-#;bRMru7ZNv)x7rmApy_B?f!IuvEO!tnpnNJq&; zO88|vuOTWS_sd#dl{=J$esZX#>!bLo=#IWX)sKWq%h=Irjnl^p!fUhBebMgRWmVNN zFIHE-tS)^eTI#wh)cqqG^}3S#`}_8J?fZ5P?0xesZ`I&H(2DQw-!rhkKRLFjD3\n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: English (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/en/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: en\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "Invalid basic header. No credentials provided." -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "Invalid basic header. Credentials string should not contain spaces." -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "Invalid basic header. Credentials not correctly base64 encoded." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Invalid username/password." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "User inactive or deleted." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "Invalid token header. No credentials provided." -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "Invalid token header. Token string should not contain spaces." -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "Invalid token header. Token string should not contain invalid characters." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Invalid token." @@ -58,23 +58,23 @@ msgstr "Invalid token." msgid "Auth Token" msgstr "Auth Token" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "Key" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "User" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "Created" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "Token" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "Tokens" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "Not found." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "Method \"{method}\" not allowed." @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "Could not satisfy the request Accept header." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "Unsupported media type \"{media_type}\" in request." @@ -140,214 +138,201 @@ msgstr "Unsupported media type \"{media_type}\" in request." msgid "Request was throttled." msgstr "Request was throttled." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "This field is required." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "This field may not be null." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" is not a valid boolean." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "This field may not be blank." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "Ensure this field has no more than {max_length} characters." -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "Ensure this field has at least {min_length} characters." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Enter a valid email address." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "This value does not match the required pattern." -#: fields.py:730 +#: fields.py:735 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:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Enter a valid URL." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "\"{value}\" is not a valid UUID." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "Enter a valid IPv4 or IPv6 address." -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "A valid integer is required." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 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:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 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:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "String value too large." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "A valid number is required." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 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:888 -#, python-brace-format +#: fields.py:894 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:889 -#, python-brace-format +#: fields.py:895 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:1004 -#, python-brace-format +#: fields.py:1025 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:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "Expected a datetime but got a date." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 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:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "Expected a date but got a datetime." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 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:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "Duration has wrong format. Use one of these formats instead: {format}." -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" is not a valid choice." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "More than {count} items..." -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "Expected a list of items but got type \"{input_type}\"." -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "This selection may not be empty." -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "\"{input}\" is not a valid path choice." -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "No file was submitted." -#: fields.py:1348 +#: fields.py:1359 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:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "No filename could be determined." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "The submitted file is empty." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 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:1399 +#: fields.py:1410 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:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "This list may not be empty." -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "Expected a dictionary of items but got type \"{input_type}\"." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "Value must be valid JSON." -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "Submit" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "ascending" + +#: filters.py:337 +msgid "descending" +msgstr "descending" + +#: pagination.py:193 msgid "Invalid page." msgstr "Invalid page." -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "Invalid cursor" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Invalid pk \"{pk_value}\" - object does not exist." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "Incorrect type. Expected pk value, received {data_type}." @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Invalid hyperlink - Object does not exist." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Incorrect type. Expected URL string, received {data_type}." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "Object with {slug_name}={value} does not exist." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Invalid value." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Invalid data. Expected a dictionary, but got {datatype}." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "Filters" @@ -412,27 +394,23 @@ msgstr "None" msgid "No items to select." msgstr "No items to select." -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "This field must be unique." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "The fields {field_names} must make a unique set." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "This field must be unique for the \"{date_field}\" date." -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "This field must be unique for the \"{date_field}\" month." -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "This field must be unique for the \"{date_field}\" year." @@ -440,15 +418,19 @@ msgstr "This field must be unique for the \"{date_field}\" year." msgid "Invalid version in \"Accept\" header." msgstr "Invalid version in \"Accept\" header." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "Invalid version in URL path." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "Invalid version in URL path. Does not match any version namespace." + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Invalid version in hostname." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Invalid version in query parameter." diff --git a/rest_framework/locale/en_AU/LC_MESSAGES/django.mo b/rest_framework/locale/en_AU/LC_MESSAGES/django.mo index 288595123478d0817f3b1c44d2884635461fd3ca..d24f22cb4b2367d46fdbcafb48bf468210d420d8 100644 GIT binary patch delta 90 zcmbQjGKFQrWG-`ELn8%4Gb=;miF4%~Av{wnLlbQSAmH*zEH2RvDN4*M&PgoEFS1ey h$;i)5ELL#ND9S7@$xKzSDap@u0ODk*z{Woki~ykI9U%Y! delta 90 zcmbQjGKFQrWG-V}149Kv3oB!biF4%~Av|*+PulKW\n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: English (Australia) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/en_AU/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: en_AU\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/en_CA/LC_MESSAGES/django.mo b/rest_framework/locale/en_CA/LC_MESSAGES/django.mo index aee511aa1352bcb292c4d565d7c35a2259d0e06f..ef1569c0b30c91945f3c2ba99a232cec7c1602b7 100644 GIT binary patch delta 90 zcmbQpGLdD%WG-`ELn8%4Gb=;miF4%~Av{wnLlbQSAmH*zEH2RvDN4*M&PgoEFS1ey h$;i)5ELL#ND9S7@$xKzSDap@u0ODk*z{WqKi~yd<9T)%r delta 90 zcmbQpGLdD%WG-V}149Kv3oB!biF4%~Av|*+PulKWU34QXN_V diff --git a/rest_framework/locale/en_CA/LC_MESSAGES/django.po b/rest_framework/locale/en_CA/LC_MESSAGES/django.po index 2bb5b573b..144694345 100644 --- a/rest_framework/locale/en_CA/LC_MESSAGES/django.po +++ b/rest_framework/locale/en_CA/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: English (Canada) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/en_CA/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: en_CA\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/en_US/LC_MESSAGES/django.mo b/rest_framework/locale/en_US/LC_MESSAGES/django.mo index 1f28ebba03d951523b3e9fa42d14ec34fc1aa2c0..3714ec8fabd768dd705dfcfe8f58ab3b6d58faaf 100644 GIT binary patch delta 20 bcmeyx^owaiFPFKlp^<{2nU$gO#OaR#OF#z~ delta 20 bcmeyx^owaiFPE{dfuVw-g_W_z#OaR#OCtvw diff --git a/rest_framework/locale/en_US/LC_MESSAGES/django.po b/rest_framework/locale/en_US/LC_MESSAGES/django.po index f88a3e567..3733a1e33 100644 --- a/rest_framework/locale/en_US/LC_MESSAGES/django.po +++ b/rest_framework/locale/en_US/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,40 +17,40 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,212 +138,199 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -362,25 +347,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -410,27 +392,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -438,15 +416,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/es/LC_MESSAGES/django.mo b/rest_framework/locale/es/LC_MESSAGES/django.mo index fa87eb02f6d6bb983902834ff2fb54091386e228..1ddc885dee3fb7cd254ac0f3b099b02b1e3afc91 100644 GIT binary patch delta 894 zcmXZaO-NKx6u|K_g=1KlxoD~}=lNX>nr~)wYSMB>Qv*SzSCCD_BLqclV%`)EF5V&n zSAj&7h9XpMD<*=FTC_4;)60*L)&h z4~o=_$jTv+MqI}h+(83t4vQSa6gJ=lhHwr8_!49I0ngxHti;YEA{TK8M{or%qc13u z#4AB@{|^?K7-r%-_MllM(u=pz#<$pkp`)H@yhJ>Yn&>w2sf4OUPT^@hfwxefKf^x! zgej~d`wF}kaz#oR++-q*(|8u2;vjC}1&kS9C9`;r_#5g1^)({h*oFzrV=KPGL5}2v{ E2SQK@Mf7C!Xb+;H1PVeg;X`kIPuK3t=XZYh-rxD1-}y}^=aMfY`|RZs zk+p*&H6pTDEph_CVKeTcfi;Ij>M?<}n85%(K`*|>2!6$OEIBOV!A`t}{dfZx@j90J zM7r>nPwan#A`^W~Y+^S?j)+{uduZZEY{TGD#~~aboL$& z)RXW8osHF^em{g-@Pf@iE8az2Fn-+m@iJ;WhPtr_s4aN4=PGKVQu^k?(?~8kiv|uN zhbH$?TQ-dxl6*p4?+21n>`!(ERtK^ARDq}l|d^Le=v&SIw!t^+S4LxW$%$sWF56~7srvrD7tV8 z-S`O4;ZxL>tl&+oqN*~?psq8HKFu)8Kr4HPv|E<(6z(ita{sC_ad!_meNPx%!yKiZVcnVGz0j%IUl<7zrJHf$L^+04Y}d24vWXd6kV2uy3-%yp(l M%(S&s^iF#I0?BQ4Z~y=R diff --git a/rest_framework/locale/es/LC_MESSAGES/django.po b/rest_framework/locale/es/LC_MESSAGES/django.po index f443b2e28..b8a89aeb6 100644 --- a/rest_framework/locale/es/LC_MESSAGES/django.po +++ b/rest_framework/locale/es/LC_MESSAGES/django.po @@ -3,18 +3,18 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: -# nnrcschmdt , 2015 +# Ernesto Rico-Schmidt , 2015 # José Padilla , 2015 -# Miguel González , 2015 -# Miguel González , 2015-2016 +# Miguel Gonzalez , 2015 +# Miguel Gonzalez , 2015-2016 # Sergio Infante , 2015 msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 18:16+0000\n" -"Last-Translator: Miguel González \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Spanish (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -22,40 +22,40 @@ msgstr "" "Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "Cabecera básica inválida. Las credenciales no fueron suministradas." -#: authentication.py:74 +#: authentication.py:76 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:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "Cabecera básica inválida. Las credenciales incorrectamente codificadas en base64." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Nombre de usuario/contraseña inválidos." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "Usuario inactivo o borrado." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "Cabecera token inválida. Las credenciales no fueron suministradas." -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "Cabecera token inválida. La cadena token no debe contener espacios." -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "Cabecera token inválida. La cadena token no debe contener caracteres inválidos." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Token inválido." @@ -63,23 +63,23 @@ msgstr "Token inválido." msgid "Auth Token" msgstr "Token de autenticación" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "Clave" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "Usuario" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "Fecha de creación" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "Token" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "Tokens" @@ -128,7 +128,6 @@ msgid "Not found." msgstr "No encontrado." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "Método \"{method}\" no permitido." @@ -137,7 +136,6 @@ msgid "Could not satisfy the request Accept header." msgstr "No se ha podido satisfacer la solicitud de cabecera de Accept." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "Tipo de medio \"{media_type}\" incompatible en la solicitud." @@ -145,214 +143,201 @@ msgstr "Tipo de medio \"{media_type}\" incompatible en la solicitud." msgid "Request was throttled." msgstr "Solicitud fue regulada (throttled)." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "Este campo es requerido." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Este campo no puede ser nulo." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" no es un booleano válido." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "Este campo no puede estar en blanco." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 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:671 -#, python-brace-format +#: fields.py:676 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:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Introduzca una dirección de correo electrónico válida." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "Este valor no coincide con el patrón requerido." -#: fields.py:730 +#: fields.py:735 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:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Introduzca una URL válida." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "\"{value}\" no es un UUID válido." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "Introduzca una dirección IPv4 o IPv6 válida." -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "Introduzca un número entero válido." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 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:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 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:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "Cadena demasiado larga." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "Se requiere un número válido." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 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:888 -#, python-brace-format +#: fields.py:894 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:889 -#, python-brace-format +#: fields.py:895 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:1004 -#, python-brace-format +#: fields.py:1025 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:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "Se esperaba un fecha/hora en vez de una fecha." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 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:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "Se esperaba una fecha en vez de una fecha/hora." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 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:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "Duración con formato erróneo. Use uno de los siguientes formatos en su lugar: {format}." -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" no es una elección válida." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "Más de {count} elementos..." -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 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:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "Esta selección no puede estar vacía." -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "\"{input}\" no es una elección de ruta válida." -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "No se envió ningún archivo." -#: fields.py:1348 +#: fields.py:1359 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:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "No se pudo determinar un nombre de archivo." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "El archivo enviado está vació." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 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:1399 +#: fields.py:1410 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:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "Esta lista no puede estar vacía." -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "Se esperaba un diccionario de elementos en vez del tipo \"{input_type}\"." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "El valor debe ser JSON válido." -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "Enviar" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "Página inválida." -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "Cursor inválido" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Clave primaria \"{pk_value}\" inválida - objeto no existe." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "Tipo incorrecto. Se esperaba valor de clave primaria y se recibió {data_type}." @@ -369,25 +354,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Hiperenlace inválido - Objeto no existe." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Tipo incorrecto. Se esperaba una URL y se recibió {data_type}." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "Objeto con {slug_name}={value} no existe." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Valor inválido." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Datos inválidos. Se esperaba un diccionario pero es un {datatype}." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "Filtros" @@ -417,27 +399,23 @@ msgstr "Ninguno" msgid "No items to select." msgstr "No hay elementos para seleccionar." -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Este campo debe ser único." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "Los campos {field_names} deben formar un conjunto único." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 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:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "Este campo debe ser único para el mes \"{date_field}\"." -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "Este campo debe ser único para el año \"{date_field}\"." @@ -445,15 +423,19 @@ msgstr "Este campo debe ser único para el año \"{date_field}\"." msgid "Invalid version in \"Accept\" header." msgstr "Versión inválida en la cabecera \"Accept\"." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "Versión inválida en la ruta de la URL." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Versión inválida en el nombre de host." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Versión inválida en el parámetro de consulta." diff --git a/rest_framework/locale/et/LC_MESSAGES/django.mo b/rest_framework/locale/et/LC_MESSAGES/django.mo index ee9c40c2b63ff58a7f819ddf82980a809985b465..8bed3993091edd5eba9ee23912b87934b09915ee 100644 GIT binary patch delta 118 zcmbQ^GRI|uhH$32uAz~Fp_!GTv9^JsfdN;5ziv=!S!Qu&ex9yNVo9o%f{}rtnXUmu zovD?fiM9a{aQP$_m*|ERCFT|9B$nhCSt*2M!EqWfqrYrYhK!mAfdN;5ziv=!S!Qu&ex9yNVo9o%f{}rtnXUmu zojF*Y0Z^S!VsVLXNKs;5aZX}Mevy?zL}FQHYLS9}QA&PcX{CZqMLvY(kdv90UzVA; Jd7^MP9{|?VB=Z0O diff --git a/rest_framework/locale/et/LC_MESSAGES/django.po b/rest_framework/locale/et/LC_MESSAGES/django.po index 5ccd9226c..c9701cca7 100644 --- a/rest_framework/locale/et/LC_MESSAGES/django.po +++ b/rest_framework/locale/et/LC_MESSAGES/django.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Estonian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/et/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -18,40 +18,40 @@ msgstr "" "Language: et\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "Sobimatu lihtpäis. Kasutajatunnus on esitamata." -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "Sobimatu lihtpäis. Kasutajatunnus ei tohi sisaldada tühikuid." -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "Sobimatu lihtpäis. Kasutajatunnus pole korrektselt base64-kodeeritud." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Sobimatu kasutajatunnus/salasõna." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "Kasutaja on inaktiivne või kustutatud." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "Sobimatu lubakaardi päis. Kasutajatunnus on esitamata." -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "Sobimatu lubakaardi päis. Loa sõne ei tohi sisaldada tühikuid." -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Sobimatu lubakaart." @@ -59,23 +59,23 @@ msgstr "Sobimatu lubakaart." msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -124,7 +124,6 @@ msgid "Not found." msgstr "Ei leidnud." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "Meetod \"{method}\" pole lubatud." @@ -133,7 +132,6 @@ msgid "Could not satisfy the request Accept header." msgstr "Päringu Accept-päist ei suutnud täita." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "Meedia tüüpi {media_type} päringus ei toetata." @@ -141,214 +139,201 @@ msgstr "Meedia tüüpi {media_type} päringus ei toetata." msgid "Request was throttled." msgstr "Liiga palju päringuid." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "Väli on kohustuslik." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Väli ei tohi olla tühi." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" pole kehtiv kahendarv." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "See väli ei tohi olla tühi." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "Veendu, et see väli poleks pikem kui {max_length} tähemärki." -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "Veendu, et see väli oleks vähemalt {min_length} tähemärki pikk." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Sisestage kehtiv e-posti aadress." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "Väärtus ei ühti etteantud mustriga." -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "Sisestage kehtiv \"slug\", mis koosneks tähtedest, numbritest, ala- või sidekriipsudest." -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Sisestage korrektne URL." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "\"{value}\" pole kehtiv UUID." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "Sisendiks peab olema täisarv." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "Veenduge, et väärtus on väiksem kui või võrdne väärtusega {max_value}. " -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "Veenduge, et väärtus on suurem kui või võrdne väärtusega {min_value}." -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "Sõne on liiga pikk." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "Sisendiks peab olema arv." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "Veenduge, et kokku pole rohkem kui {max_digits} numbit." -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "Veenduge, et komakohti pole rohkem kui {max_decimal_places}. " -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "Veenduge, et täiskohti poleks rohkem kui {max_whole_digits}." -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "Valesti formaaditud kuupäev-kellaaeg. Kasutage mõnda neist: {format}." -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "Ootasin kuupäev-kellaaeg andmetüüpi, kuid sain hoopis kuupäeva." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "Valesti formaaditud kuupäev. Kasutage mõnda neist: {format}." -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "Ootasin kuupäeva andmetüüpi, kuid sain hoopis kuupäev-kellaaja." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "Valesti formaaditud kellaaeg. Kasutage mõnda neist: {format}." -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" on sobimatu valik." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "Ootasin kirjete järjendit, kuid sain \"{input_type}\" - tüübi." -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "Ühtegi faili ei esitatud." -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "Esitatud andmetes ei olnud faili. Kontrollige vormi kodeeringut." -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "Ei suutnud tuvastada failinime." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "Esitatud fail oli tühi." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "Veenduge, et failinimi oleks maksimaalselt {max_length} tähemärki pikk (praegu on {length})." -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "Laadige üles kehtiv pildifail. Üles laetud fail ei olnud pilt või oli see katki." -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "Ootasin kirjete sõnastikku, kuid sain \"{input_type}\"." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "Sobimatu kursor." #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Sobimatu primaarvõti \"{pk_value}\" - objekti pole olemas." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "Sobimatu andmetüüp. Ootasin primaarvõtit, sain {data_type}." @@ -365,25 +350,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Sobimatu hüperlink - objekti ei eksisteeri." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Sobimatu andmetüüp. Ootasin URLi sõne, sain {data_type}." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "Objekti {slug_name}={value} ei eksisteeri." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Sobimatu väärtus." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Sobimatud andmed. Ootasin sõnastikku, kuid sain {datatype}." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -413,27 +395,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Selle välja väärtus peab olema unikaalne." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "Veerud {field_names} peavad moodustama unikaalse hulga." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "Selle välja väärtus peab olema unikaalne veerus \"{date_field}\" märgitud kuupäeval." -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "Selle välja väärtus peab olema unikaalneveerus \"{date_field}\" märgitud kuul." -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "Selle välja väärtus peab olema unikaalneveerus \"{date_field}\" märgitud aastal." @@ -441,15 +419,19 @@ msgstr "Selle välja väärtus peab olema unikaalneveerus \"{date_field}\" märg msgid "Invalid version in \"Accept\" header." msgstr "Sobimatu versioon \"Accept\" päises." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "Sobimatu versioon URLi rajas." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Sobimatu versioon hostinimes." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Sobimatu versioon päringu parameetris." diff --git a/rest_framework/locale/fa/LC_MESSAGES/django.mo b/rest_framework/locale/fa/LC_MESSAGES/django.mo index 4b0b24bc2f1de50cd2d90d779dc1bba70ac760f6..0f9b58f3fe5acf170bbbcee2f9a590321759f36f 100644 GIT binary patch delta 89 zcmey({F`~gWG-`ELn8%4Gb=;miF4%~Av{wnLlbQSAmH*zEH2RvDN4*M&PgoEFS1ey g$;i)5ELL#ND9S7@$xKzSDap@u0ODk*z{EfQ0m^J1vj6}9 delta 89 zcmey({F`~gWG-V}149Kv3oB!biF4%~Av|*+PulKWx>U diff --git a/rest_framework/locale/fa/LC_MESSAGES/django.po b/rest_framework/locale/fa/LC_MESSAGES/django.po index 986c56594..0aa9ae4c6 100644 --- a/rest_framework/locale/fa/LC_MESSAGES/django.po +++ b/rest_framework/locale/fa/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Persian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/fa/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: fa\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/fa_IR/LC_MESSAGES/django.mo b/rest_framework/locale/fa_IR/LC_MESSAGES/django.mo index 05d849e0849e8f2a4697719b5a11a60ab4f1ffb9..9a02cb05e285cf893658f1f689c2e9d688646f25 100644 GIT binary patch delta 90 zcmeBR>0p^Knaf<)&`811%*xPs;#@gL2+!2Y&_vq+2)KL_i%WDviW2jRa}rDPi>wqv hGV*g1ixr$RiZY8!GE)_7O7e3ZfH)Z{u<;KMBLJ9m9Q^0p^Knafz$z)-=^!phiU;#@gL2+thI(>4GCE}z8W65WuZ#Ju91#FG3XD}{)} ivdq*X1^=Ry{KC>o1)GX|2+JWSGcUg^GjZb|9!3D3{vAI6 diff --git a/rest_framework/locale/fa_IR/LC_MESSAGES/django.po b/rest_framework/locale/fa_IR/LC_MESSAGES/django.po index 94388647f..75b6fd156 100644 --- a/rest_framework/locale/fa_IR/LC_MESSAGES/django.po +++ b/rest_framework/locale/fa_IR/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Persian (Iran) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/fa_IR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: fa_IR\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/fi/LC_MESSAGES/django.mo b/rest_framework/locale/fi/LC_MESSAGES/django.mo index cd904e35cd5ce2b2f7c8b2ff4128363545f3de61..cb13cdaae78d64290c050162c2ec26193513cf08 100644 GIT binary patch delta 93 zcmeD5>-5`TCdOs1YiOikXl7+-JlR1^&Jn^jwK6o(HUI)HpTy!4-H@WhyyBe1lKdho kg^-N=+{9u9=ZvDv;*!i%1)GxmTn8Xdh6-$+FP0?$0ML^iPXGV_ delta 93 zcmeD5>-5`TCdOr~Yhb8gXkle+G1);(&Jn^j2XeIyfPl*\n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Finnish (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/fi/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -19,40 +19,40 @@ msgstr "" "Language: fi\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "Epäkelpo perusotsake. Ei annettuja tunnuksia." -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "Epäkelpo perusotsake. Tunnusmerkkijono ei saa sisältää välilyöntejä." -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "Epäkelpo perusotsake. Tunnukset eivät ole base64-koodattu." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Epäkelpo käyttäjänimi tai salasana." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "Käyttäjä ei-aktiivinen tai poistettu." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "Epäkelpo Token-otsake. Ei annettuja tunnuksia." -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "Epäkelpo Token-otsake. Tunnusmerkkijono ei saa sisältää välilyöntejä." -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "Epäkelpo Token-otsake. Tunnusmerkkijono ei saa sisältää epäkelpoja merkkejä." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Epäkelpo Token." @@ -60,23 +60,23 @@ msgstr "Epäkelpo Token." msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -125,7 +125,6 @@ msgid "Not found." msgstr "Ei löydy." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "Metodi \"{method}\" ei ole sallittu." @@ -134,7 +133,6 @@ msgid "Could not satisfy the request Accept header." msgstr "Ei voitu vastata pyynnön Accept-otsakkeen mukaisesti." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "Pyynnön mediatyyppiä \"{media_type}\" ei tueta." @@ -142,214 +140,201 @@ msgstr "Pyynnön mediatyyppiä \"{media_type}\" ei tueta." msgid "Request was throttled." msgstr "Pyyntö hidastettu." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "Tämä kenttä vaaditaan." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Tämän kentän arvo ei voi olla \"null\"." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" ei ole kelvollinen totuusarvo." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "Tämä kenttä ei voi olla tyhjä." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "Arvo saa olla enintään {max_length} merkkiä pitkä." -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "Arvo tulee olla vähintään {min_length} merkkiä pitkä." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Syötä kelvollinen sähköpostiosoite." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "Arvo ei täsmää vaadittuun kuvioon." -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "Tässä voidaan käyttää vain kirjaimia (a-z), numeroita (0-9) sekä ala- ja tavuviivoja (_ -)." -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Syötä oikea URL-osoite." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "{value} ei ole kelvollinen UUID." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "Syötä kelvollinen IPv4- tai IPv6-osoite." -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "Syötä kelvollinen kokonaisluku." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "Tämän arvon on oltava enintään {max_value}." -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "Tämän luvun on oltava vähintään {min_value}." -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "Liian suuri merkkijonoarvo." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "Kelvollinen luku vaaditaan." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "Tässä luvussa voi olla yhteensä enintään {max_digits} numeroa." -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "Tässä luvussa saa olla enintään {max_decimal_places} desimaalia." -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "Tässä luvussa saa olla enintään {max_whole_digits} numeroa ennen desimaalipilkkua." -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "Virheellinen päivämäärän/ajan muotoilu. Käytä jotain näistä muodoista: {format}" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "Odotettiin päivämäärää ja aikaa, saatiin vain päivämäärä." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "Virheellinen päivämäärän muotoilu. Käytä jotain näistä muodoista: {format}" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "Odotettiin päivämäärää, saatiin päivämäärä ja aika." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "Virheellinen kellonajan muotoilu. Käytä jotain näistä muodoista: {format}" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "Virheellinen keston muotoilu. Käytä jotain näistä muodoista: {format}" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" ei ole kelvollinen valinta." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "Enemmän kuin {count} kappaletta..." -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "Odotettiin listaa, saatiin tyyppi {input_type}." -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "Valinta ei saa olla tyhjä." -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "\"{input}\" ei ole kelvollinen polku." -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "Yhtään tiedostoa ei ole lähetetty." -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "Tiedostoa ei lähetetty. Tarkista lomakkeen koodaus (encoding)." -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "Tiedostonimeä ei voitu päätellä." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "Lähetetty tiedosto on tyhjä." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "Varmista että tiedostonimi on enintään {max_length} merkkiä pitkä (nyt {length})." -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "Kuva ei kelpaa. Lähettämäsi tiedosto ei ole kuva, tai tiedosto on vioittunut." -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "Lista ei saa olla tyhjä." -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "Odotettiin sanakirjaa, saatiin tyyppi {input_type}." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "Arvon pitää olla kelvollista JSONia." -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "Lähetä" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "Epäkelpo kursori" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Epäkelpo pääavain {pk_value} - objektia ei ole olemassa." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "Väärä tyyppi. Odotettiin pääavainarvoa, saatiin {data_type}." @@ -366,25 +351,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Epäkelpo linkki - objektia ei ole." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Epäkelpo tyyppi. Odotettiin URL-merkkijonoa, saatiin {data_type}." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "Objektia ({slug_name}={value}) ei ole." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Epäkelpo arvo." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Odotettiin sanakirjaa, saatiin tyyppi {datatype}." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "Suotimet" @@ -414,27 +396,23 @@ msgstr "Ei mitään" msgid "No items to select." msgstr "Ei valittavia kohteita." -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Arvon tulee olla uniikki." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "Kenttien {field_names} tulee muodostaa uniikki joukko." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "Kentän tulee olla uniikki päivämäärän {date_field} suhteen." -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "Kentän tulee olla uniikki kuukauden {date_field} suhteen." -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "Kentän tulee olla uniikki vuoden {date_field} suhteen." @@ -442,15 +420,19 @@ msgstr "Kentän tulee olla uniikki vuoden {date_field} suhteen." msgid "Invalid version in \"Accept\" header." msgstr "Epäkelpo versio Accept-otsakkeessa." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "Epäkelpo versio URL-polussa." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Epäkelpo versio palvelinosoitteessa." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Epäkelpo versio kyselyparametrissa." diff --git a/rest_framework/locale/fr/LC_MESSAGES/django.mo b/rest_framework/locale/fr/LC_MESSAGES/django.mo index 531cc46b5dec5a0acc874182e1be8111759ecb4b..2bc60c63a1e7c38332b1c06124ebff5b552c2e06 100644 GIT binary patch delta 93 zcmbOfGAU$(xj2`(uAz~Fp_!GT@nlDFIY$WB)XLCA+W-i-d=iUGbVG^~^NMp4OY)1X k6hboca}$ddoHL3ti%T+76>LiKa~*&<87i=Ofq0P+0Nkn_eE\n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: French (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -21,40 +21,40 @@ msgstr "" "Language: fr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "En-tête « basic » non valide. Informations d'identification non fournies." -#: authentication.py:74 +#: authentication.py:76 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:80 +#: authentication.py:82 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:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Nom d'utilisateur et/ou mot de passe non valide(s)." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "Utilisateur inactif ou supprimé." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "En-tête « token » non valide. Informations d'identification non fournies." -#: authentication.py:176 +#: authentication.py:179 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:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "En-tête « token » non valide. Un token ne doit pas contenir de caractères invalides." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Token non valide." @@ -62,23 +62,23 @@ msgstr "Token non valide." msgid "Auth Token" msgstr "Jeton d'authentification" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "Clef" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "Utilisateur" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "Création" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "Jeton" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "Jetons" @@ -127,7 +127,6 @@ msgid "Not found." msgstr "Pas trouvé." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "Méthode \"{method}\" non autorisée." @@ -136,7 +135,6 @@ msgid "Could not satisfy the request Accept header." msgstr "L'en-tête « Accept » n'a pas pu être satisfaite." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "Type de média \"{media_type}\" non supporté." @@ -144,214 +142,201 @@ msgstr "Type de média \"{media_type}\" non supporté." msgid "Request was throttled." msgstr "Requête ralentie." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "Ce champ est obligatoire." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Ce champ ne peut être nul." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" n'est pas un booléen valide." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "Ce champ ne peut être vide." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 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:671 -#, python-brace-format +#: fields.py:676 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:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Saisissez une adresse email valable." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "Cette valeur ne satisfait pas le motif imposé." -#: fields.py:730 +#: fields.py:735 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:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Saisissez une URL valide." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "\"{value}\" n'est pas un UUID valide." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "Saisissez une adresse IPv4 ou IPv6 valide." -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "Un nombre entier valide est requis." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 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:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 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:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "Chaîne de caractères trop longue." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "Un nombre valide est requis." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 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:888 -#, python-brace-format +#: fields.py:894 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:889 -#, python-brace-format +#: fields.py:895 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:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "La date + heure n'a pas le bon format. Utilisez un des formats suivants : {format}." -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "Attendait une date + heure mais a reçu une date." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "La date n'a pas le bon format. Utilisez un des formats suivants : {format}." -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "Attendait une date mais a reçu une date + heure." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "L'heure n'a pas le bon format. Utilisez un des formats suivants : {format}." -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "La durée n'a pas le bon format. Utilisez l'un des formats suivants: {format}." -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" n'est pas un choix valide." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "Plus de {count} éléments..." -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "Attendait une liste d'éléments mais a reçu \"{input_type}\"." -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "Cette sélection ne peut être vide." -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "\"{input}\" n'est pas un choix de chemin valide." -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "Aucun fichier n'a été soumis." -#: fields.py:1348 +#: fields.py:1359 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:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "Le nom de fichier n'a pu être déterminé." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "Le fichier soumis est vide." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 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:1399 +#: fields.py:1410 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:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "Cette liste ne peut pas être vide." -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "Attendait un dictionnaire d'éléments mais a reçu \"{input_type}\"." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "La valeur doit être un JSON valide." -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "Envoyer" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "Page invalide." -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "Curseur non valide" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Clé primaire \"{pk_value}\" non valide - l'objet n'existe pas." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "Type incorrect. Attendait une clé primaire, a reçu {data_type}." @@ -368,25 +353,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Lien non valide : l'objet n'existe pas." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Type incorrect. Attendait une URL, a reçu {data_type}." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "L'object avec {slug_name}={value} n'existe pas." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Valeur non valide." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Donnée non valide. Attendait un dictionnaire, a reçu {datatype}." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "Filtres" @@ -416,27 +398,23 @@ msgstr "Aucune" msgid "No items to select." msgstr "Aucun élément à sélectionner." -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Ce champ doit être unique." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "Les champs {field_names} doivent former un ensemble unique." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "Ce champ doit être unique pour la date \"{date_field}\"." -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "Ce champ doit être unique pour le mois \"{date_field}\"." -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "Ce champ doit être unique pour l'année \"{date_field}\"." @@ -444,15 +422,19 @@ msgstr "Ce champ doit être unique pour l'année \"{date_field}\"." msgid "Invalid version in \"Accept\" header." msgstr "Version non valide dans l'en-tête « Accept »." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "Version non valide dans l'URL." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Version non valide dans le nom d'hôte." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Version non valide dans le paramètre de requête." diff --git a/rest_framework/locale/fr_CA/LC_MESSAGES/django.mo b/rest_framework/locale/fr_CA/LC_MESSAGES/django.mo index cd0a91340e7cd76179136ab12b81d454e887b7cd..1771787f8a38aee2306456baddf8901ccb76ce34 100644 GIT binary patch delta 90 zcmeBY>1UZRnaf<)&`811%*xPs;#@gL2+!2Y&_vq+2)KL_i%WDviW2jRa}rDPi>wqv hGV*g1ixr$RiZY8!GE)_7O7e3ZfH)Z{u1UZRnafz$z)-=^!phiU;#@gL2+thI(>4GCE}z8W65WuZ#Ju91#FG3XD}{)} ivdq*X1^=Ry{KC>o1)GX|2+JWSGcUg^GjZb|VMYL>>K#-7 diff --git a/rest_framework/locale/fr_CA/LC_MESSAGES/django.po b/rest_framework/locale/fr_CA/LC_MESSAGES/django.po index f26ed453b..84cbdf4cc 100644 --- a/rest_framework/locale/fr_CA/LC_MESSAGES/django.po +++ b/rest_framework/locale/fr_CA/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: French (Canada) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/fr_CA/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: fr_CA\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/gl/LC_MESSAGES/django.mo b/rest_framework/locale/gl/LC_MESSAGES/django.mo index 761037249231da752320dce282cd550027a2b9c3..030e25f9404b394648d6213e662d64c6891d9892 100644 GIT binary patch delta 90 zcmZo>X=a%)naf<)&`811%*xPs;#@gL2+!2Y&_vq+2)KL_i%WDviW2jRa}rDPi>wqv hGV*g1ixr$RiZY8!GE)_7O7e3ZfH)Z{u<;K&BLI}}9PR)B delta 90 zcmZo>X=a%)nafz$z)-=^!phiU;#@gL2+thI(>4GCE}z8W65WuZ#Ju91#FG3XD}{)} ivdq*X1^=Ry{KC>o1)GX|2+JWSGcUg^GjZb|c18f3dL1wT diff --git a/rest_framework/locale/gl/LC_MESSAGES/django.po b/rest_framework/locale/gl/LC_MESSAGES/django.po index ba0b788fc..5ec55729e 100644 --- a/rest_framework/locale/gl/LC_MESSAGES/django.po +++ b/rest_framework/locale/gl/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Galician (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/gl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: gl\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/gl_ES/LC_MESSAGES/django.mo b/rest_framework/locale/gl_ES/LC_MESSAGES/django.mo index 281b6e66b7e66e840451489ae54e0fd19a7deba4..90c4212bacbc7d7515ae06b7fd6ff961fe04394f 100644 GIT binary patch delta 91 zcmbQsI+u0ACoXecLn8%4Gb=;miGSoAAv{wnLlbQSAmH*zEH2RvDN4*M&PgoEFS1ey i$;i)5ELL#ND9S7@$xKzSDap@u0ODk*z~*R1Cq@AM&>epO delta 91 zcmbQsI+u0ACoW@M149Kv3oB!biGSoAAv|*+PulKW\n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Galician (Spain) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/gl_ES/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -18,40 +18,40 @@ msgstr "" "Language: gl_ES\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -59,23 +59,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -124,7 +124,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -133,7 +132,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -141,214 +139,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -365,25 +350,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Valor non válido." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -413,27 +395,23 @@ msgstr "Ningún" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -441,15 +419,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/he_IL/LC_MESSAGES/django.mo b/rest_framework/locale/he_IL/LC_MESSAGES/django.mo index feef64e1b36c9be38b32bf05c413c471116f8199..55ffe5403c8cf163011f5511c6f3a6b5209be7d5 100644 GIT binary patch delta 90 zcmbQhGJ$2nWG-`ELn8%4Gb=;miF4%~Av{wnLlbQSAmH*zEH2RvDN4*M&PgoEFS1ey h$;i)5ELL#ND9S7@$xKzSDap@u0ODk*z{Wo!i~yb#9Tflo delta 90 zcmbQhGJ$2nWG-V}149Kv3oB!biF4%~Av|*+PulKWK|2o*h>J diff --git a/rest_framework/locale/he_IL/LC_MESSAGES/django.po b/rest_framework/locale/he_IL/LC_MESSAGES/django.po index cee35fedc..686ae6fa7 100644 --- a/rest_framework/locale/he_IL/LC_MESSAGES/django.po +++ b/rest_framework/locale/he_IL/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Hebrew (Israel) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/he_IL/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: he_IL\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/hu/LC_MESSAGES/django.mo b/rest_framework/locale/hu/LC_MESSAGES/django.mo index 9053ad62fefb8c069bc5eaafbfcfbb5be1cf8948..cb27fb740a8f8efca1fd9c8264bb56df60d5b078 100644 GIT binary patch delta 75 zcmZ4NzSw<3oe-C~uAz~Fp_!GT@#GF6IVjiE%Ftx;1|e~Ukc|A?#9{^KjH1lqlFU>E To09xo2Ov&{3T%EZv_}8{Ou-ng delta 75 zcmZ4NzSw<3oe-C?u7RO~p@o&P#pDhlIVjg0#N8kyt`Lz}mYG_l;9r!IUszhHU{jF~ TVL9Yv=H-`VCT@N%v_}8{Q#Kgz diff --git a/rest_framework/locale/hu/LC_MESSAGES/django.po b/rest_framework/locale/hu/LC_MESSAGES/django.po index 669a7bffa..7f3081fff 100644 --- a/rest_framework/locale/hu/LC_MESSAGES/django.po +++ b/rest_framework/locale/hu/LC_MESSAGES/django.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Hungarian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/hu/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -18,40 +18,40 @@ msgstr "" "Language: hu\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "Érvénytelen basic fejlécmező. Nem voltak megadva azonosítók." -#: authentication.py:74 +#: authentication.py:76 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:80 +#: authentication.py:82 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:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Érvénytelen felhasználónév/jelszó." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "A felhasználó nincs aktiválva vagy törölve lett." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "Érvénytelen token fejlécmező. Nem voltak megadva azonosítók." -#: authentication.py:176 +#: authentication.py:179 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:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Érvénytelen token." @@ -59,23 +59,23 @@ msgstr "Érvénytelen token." msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -124,7 +124,6 @@ msgid "Not found." msgstr "Nem található." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "A \"{method}\" metódus nem megengedett." @@ -133,7 +132,6 @@ msgid "Could not satisfy the request Accept header." msgstr "A kérés Accept fejlécmezőjét nem lehetett kiszolgálni." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "Nem támogatott média típus \"{media_type}\" a kérésben." @@ -141,214 +139,201 @@ msgstr "Nem támogatott média típus \"{media_type}\" a kérésben." msgid "Request was throttled." msgstr "A kérés korlátozva lett." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "Ennek a mezőnek a megadása kötelező." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Ez a mező nem lehet null értékű." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "Az \"{input}\" nem egy érvényes logikai érték." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "Ez a mező nem lehet üres." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 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:671 -#, python-brace-format +#: fields.py:676 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:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Adjon meg egy érvényes e-mail címet!" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "Ez az érték nem illeszkedik a szükséges mintázatra." -#: fields.py:730 +#: fields.py:735 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:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Adjon meg egy érvényes URL-t!" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "Egy érvényes egész szám megadása szükséges." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 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:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 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:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "A karakterlánc túl hosszú." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "Egy érvényes szám megadása szükséges." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 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:888 -#, python-brace-format +#: fields.py:894 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:889 -#, python-brace-format +#: fields.py:895 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:1004 -#, python-brace-format +#: fields.py:1025 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:1005 +#: fields.py:1026 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:1082 -#, python-brace-format +#: fields.py:1103 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:1083 +#: fields.py:1104 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:1151 -#, python-brace-format +#: fields.py:1170 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:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "Az \"{input}\" nem egy érvényes elem." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "Elemek listája helyett \"{input_type}\" lett elküldve." -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "Semmilyen fájl sem került feltöltésre." -#: fields.py:1348 +#: fields.py:1359 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:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "A fájlnév nem megállapítható." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "A küldött fájl üres." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 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:1399 +#: fields.py:1410 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:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Érvénytelen pk \"{pk_value}\" - az objektum nem létezik." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "Helytelen típus. pk érték helyett {data_type} lett elküldve." @@ -365,25 +350,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Érvénytelen link - Az objektum nem létezik." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Helytelen típus. URL karakterlánc helyett {data_type} lett elküldve." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "Nem létezik olyan objektum, amelynél {slug_name}={value}." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Érvénytelen érték." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Érvénytelen adat. Egy dictionary helyett {datatype} lett elküldve." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -413,27 +395,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Ennek a mezőnek egyedinek kell lennie." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "A {field_names} mezőnevek nem tartalmazhatnak duplikátumot." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 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:241 -#, python-brace-format +#: validators.py:260 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:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "A mezőnek egyedinek kell lennie a \"{date_field}\" évre." @@ -441,15 +419,19 @@ msgstr "A mezőnek egyedinek kell lennie a \"{date_field}\" évre." msgid "Invalid version in \"Accept\" header." msgstr "Érvénytelen verzió az \"Accept\" fejlécmezőben." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "Érvénytelen verzió az URL elérési útban." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Érvénytelen verzió a hosztnévben." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Érvénytelen verzió a lekérdezési paraméterben." diff --git a/rest_framework/locale/id/LC_MESSAGES/django.mo b/rest_framework/locale/id/LC_MESSAGES/django.mo index 350d64a3b89e2d42301c885b4d4dc018ea2b21e3..beb9643b0570cfa42a3e5fad53aee3664ce79141 100644 GIT binary patch delta 90 zcmeyz{EvCUWG-`ELn8%4Gb=;miF4%~Av{wnLlbQSAmH*zEH2RvDN4*M&PgoEFS1ey h$;i)5ELL#ND9S7@$xKzSDap@u0ODk*z{WpJi~#Ap9o+x` delta 90 zcmeyz{EvCUWG-V}149Kv3oB!biF4%~Av|*+PulKW2=@Mjjsk diff --git a/rest_framework/locale/id/LC_MESSAGES/django.po b/rest_framework/locale/id/LC_MESSAGES/django.po index 1137755c8..c84add0a4 100644 --- a/rest_framework/locale/id/LC_MESSAGES/django.po +++ b/rest_framework/locale/id/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Indonesian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/id/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: id\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/it/LC_MESSAGES/django.mo b/rest_framework/locale/it/LC_MESSAGES/django.mo index 8af8bc74694ef0ffc80d50e8c594e49bc6636d0c..5d52d3dcfde88ac19716f27a7d8c3904b3987413 100644 GIT binary patch delta 93 zcmew!@I7FInHZP3uAz~Fp_!GT@ni=vIY$WB)XLCA+W-i-d=iUGbVG^~^NMp4OY)1X k6hboca}$ddoHL3ti%T+76>LiKa~*&<87i=OzSs>x03rJxo&W#< delta 93 zcmew!@I7FInHZO`u7RO~p@o&P#bgIDIY$WB9LUu+00J(b#Nra&kfOxA;+({i{30ub lh{UqY)FK7{qLlo?(n\n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Italian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/it/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -21,40 +21,40 @@ msgstr "" "Language: it\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "Header di base invalido. Credenziali non fornite." -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "Header di base invalido. Le credenziali non dovrebbero contenere spazi." -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "Credenziali non correttamente codificate in base64." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Nome utente/password non validi" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "Utente inattivo o eliminato." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "Header del token non valido. Credenziali non fornite." -#: authentication.py:176 +#: authentication.py:179 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:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "Header del token invalido. La stringa del token non dovrebbe contenere caratteri illegali." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Token invalido." @@ -62,23 +62,23 @@ msgstr "Token invalido." msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -127,7 +127,6 @@ msgid "Not found." msgstr "Non trovato." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "Metodo \"{method}\" non consentito" @@ -136,7 +135,6 @@ msgid "Could not satisfy the request Accept header." msgstr "Impossibile soddisfare l'header \"Accept\" presente nella richiesta." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "Tipo di media \"{media_type}\"non supportato." @@ -144,214 +142,201 @@ msgstr "Tipo di media \"{media_type}\"non supportato." msgid "Request was throttled." msgstr "La richiesta è stata limitata (throttled)." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "Campo obbligatorio." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Il campo non può essere nullo." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" non è un valido valore booleano." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "Questo campo non può essere omesso." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 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:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "Assicurati che questo campo abbia almeno {min_length} caratteri." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Inserisci un indirizzo email valido." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "Questo valore non corrisponde alla sequenza richiesta." -#: fields.py:730 +#: fields.py:735 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:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Inserisci un URL valido" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "\"{value}\" non è un UUID valido." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "Inserisci un indirizzo IPv4 o IPv6 valido." -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "È richiesto un numero intero valido." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 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:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 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:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "Stringa troppo lunga." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "È richiesto un numero valido." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 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:888 -#, python-brace-format +#: fields.py:894 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:889 -#, python-brace-format +#: fields.py:895 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:1004 -#, python-brace-format +#: fields.py:1025 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:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "Atteso un oggetto di tipo datetime ma l'oggetto ricevuto è di tipo date." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 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:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "Atteso un oggetto di tipo date ma l'oggetto ricevuto è di tipo datetime." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "L'orario ha un formato errato. Usa uno dei seguenti formati: {format}." -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "La durata è in un formato errato. Usa uno dei seguenti formati: {format}." -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" non è una scelta valida." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "Più di {count} oggetti..." -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 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:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "Questa selezione potrebbe non essere vuota." -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "\"{input}\" non è un percorso valido." -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "Non è stato inviato alcun file." -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "I dati inviati non corrispondono ad un file. Si prega di controllare il tipo di codifica nel form." -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "Il nome del file non può essere determinato." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "Il file inviato è vuoto." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "Assicurati che il nome del file abbia, al più, {max_length} caratteri (attualmente ne ha {length})." -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "Invia un'immagine valida. Il file che hai inviato non era un'immagine o era corrotto." -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "Questa lista potrebbe non essere vuota." -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "Era atteso un dizionario di oggetti ma il dato ricevuto è di tipo \"{input_type}\"." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "Il valore deve essere un JSON valido." -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "Invia" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "Cursore non valido" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Pk \"{pk_value}\" non valido - l'oggetto non esiste." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "Tipo non corretto. Era atteso un valore pk, ma è stato ricevuto {data_type}." @@ -368,25 +353,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Collegamento non valido - L'oggetto non esiste." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Tipo non corretto. Era attesa una stringa URL, ma è stato ricevuto {data_type}." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "L'oggetto con {slug_name}={value} non esiste." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Valore non valido." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Dati non validi. Era atteso un dizionario, ma si è ricevuto {datatype}." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "Filtri" @@ -416,27 +398,23 @@ msgstr "Nessuno" msgid "No items to select." msgstr "Nessun elemento da selezionare." -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Questo campo deve essere unico." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "I campi {field_names} devono costituire un insieme unico." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "Questo campo deve essere unico per la data \"{date_field}\"." -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "Questo campo deve essere unico per il mese \"{date_field}\"." -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "Questo campo deve essere unico per l'anno \"{date_field}\"." @@ -444,15 +422,19 @@ msgstr "Questo campo deve essere unico per l'anno \"{date_field}\"." msgid "Invalid version in \"Accept\" header." msgstr "Versione non valida nell'header \"Accept\"." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "Versione non valida nella sequenza URL." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Versione non valida nel nome dell'host." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Versione non valida nel parametro della query." diff --git a/rest_framework/locale/ja/LC_MESSAGES/django.mo b/rest_framework/locale/ja/LC_MESSAGES/django.mo index 048da56fd3776f05337b9498f6444bf9546a7a85..1f934cc378a47eb5d4f15aec1e1bde9e58f6acf2 100644 GIT binary patch delta 93 zcmZ1)yEJwKqd1qjuAz~Fp_!GT@nmjsIY$WB)XLCA+W-i-d=iUGbVG^~^NMp4OY)1X k6hboca}$ddoHL3ti%T+76>LiKa~*&<87i\n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Japanese (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/ja/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -18,40 +18,40 @@ msgstr "" "Language: ja\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "不正な基本ヘッダです。認証情報が含まれていません。" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "不正な基本ヘッダです。認証情報文字列に空白を含めてはいけません。" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "不正な基本ヘッダです。認証情報がBASE64で正しくエンコードされていません。" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "ユーザ名かパスワードが違います。" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "ユーザが無効か削除されています。" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "不正なトークンヘッダです。認証情報が含まれていません。" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "不正なトークンヘッダです。トークン文字列に空白を含めてはいけません。" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "不正なトークンヘッダです。トークン文字列に不正な文字を含めてはいけません。" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "不正なトークンです。" @@ -59,23 +59,23 @@ msgstr "不正なトークンです。" msgid "Auth Token" msgstr "認証トークン" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "キー" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "ユーザ" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "作成された" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "トークン" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "トークン" @@ -124,7 +124,6 @@ msgid "Not found." msgstr "見つかりませんでした。" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "メソッド \"{method}\" は許されていません。" @@ -133,7 +132,6 @@ msgid "Could not satisfy the request Accept header." msgstr "リクエストのAcceptヘッダを満たすことができませんでした。" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "リクエストのメディアタイプ \"{media_type}\" はサポートされていません。" @@ -141,214 +139,201 @@ msgstr "リクエストのメディアタイプ \"{media_type}\" はサポート msgid "Request was throttled." msgstr "リクエストの処理は絞られました。" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "この項目は必須です。" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "この項目はnullにできません。" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" は有効なブーリアンではありません。" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "この項目は空にできません。" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "この項目が{max_length}文字より長くならないようにしてください。" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "この項目は少なくとも{min_length}文字以上にしてください。" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "有効なメールアドレスを入力してください。" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "この値は所要のパターンにマッチしません。" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "文字、数字、アンダースコア、またはハイフンから成る有効な \"slug\" を入力してください。" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "有効なURLを入力してください。" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "\"{value}\" は有効なUUIDではありません。" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "有効なIPv4またはIPv6アドレスを入力してください。" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "有効な整数を入力してください。" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "この値は{max_value}以下にしてください。" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "この値は{min_value}以上にしてください。" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "文字列が長過ぎます。" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "有効な数値を入力してください。" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "合計で最大{max_digits}桁以下になるようにしてください。" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "小数点以下の桁数を{max_decimal_places}を超えないようにしてください。" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "整数部の桁数を{max_whole_digits}を超えないようにしてください。" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "日時の形式が違います。以下のどれかの形式にしてください: {format}。" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "日付ではなく日時を入力してください。" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "日付の形式が違います。以下のどれかの形式にしてください: {format}。" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "日時ではなく日付を入力してください。" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "時刻の形式が違います。以下のどれかの形式にしてください: {format}。" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "機関の形式が違います。以下のどれかの形式にしてください: {format}。" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\"は有効な選択肢ではありません。" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr " {count} 個より多い..." -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "\"{input_type}\" 型のデータではなく項目のリストを入力してください。" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "空でない項目を選択してください。" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "\"{input}\"は有効なパスの選択肢ではありません。" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "ファイルが添付されていません。" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "添付されたデータはファイルではありません。フォームのエンコーディングタイプを確認してください。" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "ファイル名が取得できませんでした。" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "添付ファイルの中身が空でした。" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "ファイル名は最大{max_length}文字にしてください({length}文字でした)。" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "有効な画像をアップロードしてください。アップロードされたファイルは画像でないか壊れた画像です。" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "リストは空ではいけません。" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "\"{input_type}\" 型のデータではなく項目の辞書を入力してください。" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "値は有効なJSONでなければなりません。" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "提出" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "カーソルが不正です。" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "主キー \"{pk_value}\" は不正です - データが存在しません。" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "不正な型です。{data_type} 型ではなく主キーの値を入力してください。" @@ -365,25 +350,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "ハイパーリンクが不正です - リンク先が存在しません。" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "不正なデータ型です。{data_type} 型ではなくURL文字列を入力してください。" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "{slug_name}={value} のデータが存在しません。" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "不正な値です。" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "不正なデータです。{datatype} 型ではなく辞書を入力してください。" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "フィルタ" @@ -413,27 +395,23 @@ msgstr "なし" msgid "No items to select." msgstr "選択する項目がありません。" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "この項目は一意でなければなりません。" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "項目 {field_names} は一意な組でなければなりません。" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "この項目は \"{date_field}\" の日に対して一意でなければなりません。" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "この項目は \"{date_field}\" の月に対して一意でなければなりません。" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "この項目は \"{date_field}\" の年に対して一意でなければなりません。" @@ -441,15 +419,19 @@ msgstr "この項目は \"{date_field}\" の年に対して一意でなければ msgid "Invalid version in \"Accept\" header." msgstr "\"Accept\" 内のバージョンが不正です。" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "URLパス内のバージョンが不正です。" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "ホスト名内のバージョンが不正です。" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "クエリパラメータ内のバージョンが不正です。" diff --git a/rest_framework/locale/ko_KR/LC_MESSAGES/django.mo b/rest_framework/locale/ko_KR/LC_MESSAGES/django.mo index ac88a062dc30575bd46c86f2479fcd819a420a1f..6410f0b1cd6cd733df113762a4664c01ecae63ff 100644 GIT binary patch delta 93 zcmZ4Hx6E(DW+5(fT|*-ULo+Kwu4oEH24RRj?_^&vgLeWT?Pq8DTCF053xwnE(I) delta 93 zcmZ4Hx6E(DW+5(PT?0b}LklZoi^+S1\n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Korean (Korea) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/ko_KR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -18,40 +18,40 @@ msgstr "" "Language: ko_KR\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "기본 헤더(basic header)가 유효하지 않습니다. 인증데이터(credentials)가 제공되지 않았습니다." -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "기본 헤더(basic header)가 유효하지 않습니다. 인증데이터(credentials) 문자열은 빈칸(spaces)을 포함하지 않아야 합니다." -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "기본 헤더(basic header)가 유효하지 않습니다. 인증데이터(credentials)가 base64로 적절히 부호화(encode)되지 않았습니다." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "아이디/비밀번호가 유효하지 않습니다." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "계정이 중지되었거나 삭제되었습니다." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "토큰 헤더가 유효하지 않습니다. 인증데이터(credentials)가 제공되지 않았습니다." -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "토큰 헤더가 유효하지 않습니다. 토큰 문자열은 빈칸(spaces)를 포함하지 않아야 합니다." -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "토큰 헤더가 유효하지 않습니다. 토큰 문자열은 유효하지 않은 문자를 포함하지 않아야 합니다." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "토큰이 유효하지 않습니다." @@ -59,23 +59,23 @@ msgstr "토큰이 유효하지 않습니다." msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -124,7 +124,6 @@ msgid "Not found." msgstr "찾을 수 없습니다." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "메소드(Method) \"{method}\"는 허용되지 않습니다." @@ -133,7 +132,6 @@ msgid "Could not satisfy the request Accept header." msgstr "Accept header 요청을 만족할 수 없습니다." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "요청된 \"{media_type}\"가 지원되지 않는 미디어 형태입니다." @@ -141,214 +139,201 @@ msgstr "요청된 \"{media_type}\"가 지원되지 않는 미디어 형태입니 msgid "Request was throttled." msgstr "요청이 지연(throttled)되었습니다." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "이 항목을 채워주십시오." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "이 칸은 null일 수 없습니다." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\"이 유효하지 않은 부울(boolean)입니다." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "이 칸은 blank일 수 없습니다." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "이 칸이 글자 수가 {max_length} 이하인지 확인하십시오." -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "이 칸이 글자 수가 적어도 {min_length} 이상인지 확인하십시오." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "유효한 이메일 주소를 입력하십시오." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "형식에 맞지 않는 값입니다." -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "문자, 숫자, 밑줄( _ ) 또는 하이픈( - )으로 이루어진 유효한 \"slug\"를 입력하십시오." -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "유효한 URL을 입력하십시오." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "\"{value}\"가 유효하지 않은 UUID 입니다." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "유효한 IPv4 또는 IPv6 주소를 입력하십시오." -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "유효한 정수(integer)를 넣어주세요." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "이 값이 {max_value}보다 작거나 같은지 확인하십시오." -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "이 값이 {min_value}보다 크거나 같은지 확인하십시오." -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "문자열 값이 너무 큽니다." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "유효한 숫자를 넣어주세요." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "전체 숫자(digits)가 {max_digits} 이하인지 확인하십시오." -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "소수점 자릿수가 {max_decimal_places} 이하인지 확인하십시오." -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "소수점 자리 앞에 숫자(digits)가 {max_whole_digits} 이하인지 확인하십시오." -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "Datetime의 포멧이 잘못되었습니다. 이 형식들 중 한가지를 사용하세요: {format}." -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "예상된 datatime 대신 date를 받았습니다." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "Date의 포멧이 잘못되었습니다. 이 형식들 중 한가지를 사용하세요: {format}." -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "예상된 date 대신 datetime을 받았습니다." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "Time의 포멧이 잘못되었습니다. 이 형식들 중 한가지를 사용하세요: {format}." -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\"이 유효하지 않은 선택(choice)입니다." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "아이템 리스트가 예상되었으나 \"{input_type}\"를 받았습니다." -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "파일이 제출되지 않았습니다." -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "제출된 데이터는 파일이 아닙니다. 제출된 서식의 인코딩 형식을 확인하세요." -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "파일명을 알 수 없습니다." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "제출된 파일이 비어있습니다." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "이 파일명의 글자수가 최대 {max_length}를 넘지 않는지 확인하십시오. (이것은 {length}가 있습니다)." -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "유효한 이미지 파일을 업로드 하십시오. 업로드 하신 파일은 이미지 파일이 아니거나 손상된 이미지 파일입니다." -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "아이템 딕셔너리가 예상되었으나 \"{input_type}\" 타입을 받았습니다." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "커서(cursor)가 유효하지 않습니다." #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "유효하지 않은 pk \"{pk_value}\" - 객체가 존재하지 않습니다." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "잘못된 형식입니다. pk 값 대신 {data_type}를 받았습니다." @@ -365,25 +350,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "유효하지 않은 하이퍼링크 - 객체가 존재하지 않습니다." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "잘못된 형식입니다. URL 문자열을 예상했으나 {data_type}을 받았습니다." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "{slug_name}={value} 객체가 존재하지 않습니다." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "값이 유효하지 않습니다." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "유효하지 않은 데이터. 딕셔너리(dictionary)대신 {datatype}를 받았습니다." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -413,27 +395,23 @@ msgstr "" msgid "No items to select." msgstr "선택할 아이템이 없습니다." -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "이 칸은 반드시 고유해야 합니다." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -441,15 +419,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "\"Accept\" header내 버전이 유효하지 않습니다." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "URL path내 버전이 유효하지 않습니다." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "hostname내 버전이 유효하지 않습니다." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "쿼리 파라메터내 버전이 유효하지 않습니다." diff --git a/rest_framework/locale/mk/LC_MESSAGES/django.mo b/rest_framework/locale/mk/LC_MESSAGES/django.mo index e2a518f71325e547319530bac20437003629b347..ac9a48193d7df5680b8e91070daa80cbdbc1713f 100644 GIT binary patch delta 75 zcmew#^gn1roe-C~uAz~Fp_!GT@#GF6IVjiE%Ftx;1|e~Ukc|A?#9{^KjH1lqlFU>E To09xo2Ov&{3T%EZG(!vkhxHj= delta 75 zcmew#^gn1roe-C?u7RO~p@o&P#pDhlIVjg0#N8kyt`Lz}mYG_l;9r!IUszhHU{jF~ TVL9Yv=H-`VCT@N%G(!vkj%pd8 diff --git a/rest_framework/locale/mk/LC_MESSAGES/django.po b/rest_framework/locale/mk/LC_MESSAGES/django.po index 5818124ae..d53a30677 100644 --- a/rest_framework/locale/mk/LC_MESSAGES/django.po +++ b/rest_framework/locale/mk/LC_MESSAGES/django.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Macedonian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/mk/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -18,40 +18,40 @@ msgstr "" "Language: mk\n" "Plural-Forms: nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "Невалиден основен header. Не се внесени податоци за автентикација." -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "Невалиден основен header. Автентикационата низа не треба да содржи празни места." -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "Невалиден основен header. Податоците за автентикација не се енкодирани со base64." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Невалидно корисничко име/лозинка." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "Корисникот е деактивиран или избришан." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "Невалиден токен header. Не се внесени податоци за најава." -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "Невалиден токен во header. Токенот не треба да содржи празни места." -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Невалиден токен." @@ -59,23 +59,23 @@ msgstr "Невалиден токен." msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -124,7 +124,6 @@ msgid "Not found." msgstr "Не е пронајдено ништо." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "Методата \"{method}\" не е дозволена." @@ -133,7 +132,6 @@ msgid "Could not satisfy the request Accept header." msgstr "Не може да се исполни барањето на Accept header-от." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "Media типот „{media_type}“ не е поддржан." @@ -141,214 +139,201 @@ msgstr "Media типот „{media_type}“ не е поддржан." msgid "Request was throttled." msgstr "Request-от е забранет заради ограничувања." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "Ова поле е задолжително." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Ова поле не смее да биде недефинирано." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" не е валиден boolean." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "Ова поле не смее да биде празно." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "Ова поле не смее да има повеќе од {max_length} знаци." -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "Ова поле мора да има барем {min_length} знаци." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Внесете валидна email адреса." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "Ова поле не е по правилната шема/барање." -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "Внесете валидно име што содржи букви, бројки, долни црти или црти." -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Внесете валиден URL." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "Задолжителен е валиден цел број." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "Вредноста треба да биде помала или еднаква на {max_value}." -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "Вредноста треба да биде поголема или еднаква на {min_value}." -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "Вредноста е преголема." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "Задолжителен е валиден број." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "Не смее да има повеќе од {max_digits} цифри вкупно." -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "Не смее да има повеќе од {max_decimal_places} децимални места." -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "Не смее да има повеќе од {max_whole_digits} цифри пред децималната точка." -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "Датата и времето се со погрешен формат. Користете го овој формат: {format}." -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "Очекувано беше дата и време, а внесено беше само дата." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "Датата е со погрешен формат. Користете го овој формат: {format}." -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "Очекувана беше дата, а внесени беа и дата и време." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "Времето е со погрешен формат. Користете го овој формат: {format}." -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "„{input}“ не е валиден избор." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "Очекувана беше листа, а внесено беше „{input_type}“." -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "Ниеден фајл не е качен (upload-иран)." -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "Испратените податоци не се фајл. Проверете го encoding-от на формата." -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "Не може да се открие име на фајлот." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "Качениот (upload-иран) фајл е празен." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "Името на фајлот треба да има највеќе {max_length} знаци (а има {length})." -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "Качете (upload-ирајте) валидна слика. Фајлот што го качивте не е валидна слика или е расипан." -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Невалиден pk „{pk_value}“ - објектот не постои." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "Неточен тип. Очекувано беше pk, а внесено {data_type}." @@ -365,25 +350,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Невалиден хиперлинк - Објектот не постои." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Неточен тип. Очекувано беше URL, a внесено {data_type}." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "Објектот со {slug_name}={value} не постои." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Невалидна вредност." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Невалидни податоци. Очекуван беше dictionary, а внесен {datatype}." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -413,27 +395,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Ова поле мора да биде уникатно." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "Полињата {field_names} заедно мора да формираат уникатен збир." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "Ова поле мора да биде уникатно за „{date_field}“ датата." -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "Ова поле мора да биде уникатно за „{date_field}“ месецот." -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "Ова поле мора да биде уникатно за „{date_field}“ годината." @@ -441,15 +419,19 @@ msgstr "Ова поле мора да биде уникатно за „{date_fi msgid "Invalid version in \"Accept\" header." msgstr "Невалидна верзија во „Accept“ header-от." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "Невалидна верзија во URL патеката." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Невалидна верзија во hostname-от." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Невалидна верзија во query параметарот." diff --git a/rest_framework/locale/nb/LC_MESSAGES/django.mo b/rest_framework/locale/nb/LC_MESSAGES/django.mo index a0bdb3a49184449bd82e8037f9830fba15338b75..d3dfe100a7f558c9f067d9a720b6889c1a501b9f 100644 GIT binary patch delta 93 zcmX@@bJ}NvnHZP3uAz~Fp_!GT@ni=vIY$WB)XLCA+W-i-d=iUGbVG^~^NMp4OY)1X k6hboca}$ddoHL3ti%T+76>LiKa~*&<87i=OzStUm0RI{u5C8xG delta 93 zcmX@@bJ}NvnHZO`u7RO~p@o&P#bgIDIY$WB9LUu+00J(b#Nra&kfOxA;+({i{30ub lh{UqY)FK7{qLlo?(n\n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Norwegian Bokmål (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/nb/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -18,40 +18,40 @@ msgstr "" "Language: nb\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "Ugyldig basic header. Ingen legitimasjon gitt." -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "Ugylid basic header. Legitimasjonsstreng bør ikke inneholde mellomrom." -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "Ugyldig basic header. Legitimasjonen ikke riktig Base64 kodet." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Ugyldig brukernavn eller passord." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "Bruker inaktiv eller slettet." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "Ugyldig token header. Ingen legitimasjon gitt." -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "Ugyldig token header. Token streng skal ikke inneholde mellomrom." -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "Ugyldig token header. Tokenstrengen skal ikke inneholde ugyldige tegn." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Ugyldig token." @@ -59,23 +59,23 @@ msgstr "Ugyldig token." msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -124,7 +124,6 @@ msgid "Not found." msgstr "Ikke funnet." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "Metoden \"{method}\" ikke gyldig." @@ -133,7 +132,6 @@ msgid "Could not satisfy the request Accept header." msgstr "Kunne ikke tilfredsstille request Accept header." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "Ugyldig media type \"{media_type}\" i request." @@ -141,214 +139,201 @@ msgstr "Ugyldig media type \"{media_type}\" i request." msgid "Request was throttled." msgstr "Forespørselen ble strupet." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "Dette feltet er påkrevd." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Dette feltet må ikke være tomt." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" er ikke en gyldig bolsk verdi." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "Dette feltet må ikke være blankt." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "Forsikre deg om at dette feltet ikke har mer enn {max_length} tegn." -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "Forsikre deg at dette feltet har minst {min_length} tegn." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Oppgi en gyldig epost-adresse." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "Denne verdien samsvarer ikke med de påkrevde mønsteret." -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "Skriv inn en gyldig \"slug\" som består av bokstaver, tall, understrek eller bindestrek." -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Skriv inn en gyldig URL." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "\"{value}\" er ikke en gyldig UUID." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "Skriv inn en gyldig IPv4 eller IPv6-adresse." -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "En gyldig heltall er nødvendig." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "Sikre denne verdien er mindre enn eller lik {max_value}." -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "Sikre denne verdien er større enn eller lik {min_value}." -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "Strengverdien for stor." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "Et gyldig nummer er nødvendig." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "Pass på at det ikke er flere enn {max_digits} siffer totalt." -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "Pass på at det ikke er flere enn {max_decimal_places} desimaler." -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "Pass på at det ikke er flere enn {max_whole_digits} siffer før komma." -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "Datetime har feil format. Bruk et av disse formatene i stedet: {format}." -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "Forventet en datetime, men fikk en date." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "Dato har feil format. Bruk et av disse formatene i stedet: {format}." -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "Forventet en date, men fikk en datetime." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "Tid har feil format. Bruk et av disse formatene i stedet: {format}." -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "Varighet har feil format. Bruk et av disse formatene i stedet: {format}." -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" er ikke et gyldig valg." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "Mer enn {count} elementer ..." -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "Forventet en liste over elementer, men fikk type \"{input_type}\"." -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "Dette valget kan ikke være tomt." -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "\"{input}\" er ikke en gyldig bane valg." -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "Ingen fil ble sendt." -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "De innsendte data var ikke en fil. Kontroller kodingstypen på skjemaet." -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "Kunne ikke finne filnavn." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "Den innsendte filen er tom." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "Sikre dette filnavnet har på det meste {max_length} tegn (det har {length})." -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "Last opp et gyldig bilde. Filen du lastet opp var enten ikke et bilde eller en ødelagt bilde." -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "Denne listen kan ikke være tom." -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "Forventet en dictionary av flere ting, men fikk typen \"{input_type}\"." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "Verdien må være gyldig JSON." -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "Send inn" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "Ugyldig markør" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Ugyldig pk \"{pk_value}\" - objektet eksisterer ikke." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "Feil type. Forventet pk verdi, fikk {data_type}." @@ -365,25 +350,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Ugyldig hyperkobling - Objektet eksisterer ikke." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Feil type. Forventet URL streng, fikk {data_type}." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "Objekt med {slug_name}={value} finnes ikke." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Ugyldig verdi." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Ugyldige data. Forventet en dicitonary, men fikk {datatype}." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "Filtre" @@ -413,27 +395,23 @@ msgstr "Ingen" msgid "No items to select." msgstr "Ingenting å velge." -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Dette feltet må være unikt." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "Feltene {field_names} må gjøre et unikt sett." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "Dette feltet må være unikt for \"{date_field}\" dato." -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "Dette feltet må være unikt for \"{date_field}\" måned." -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "Dette feltet må være unikt for \"{date_field}\" år." @@ -441,15 +419,19 @@ msgstr "Dette feltet må være unikt for \"{date_field}\" år." msgid "Invalid version in \"Accept\" header." msgstr "Ugyldig versjon på \"Accept\" header." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "Ugyldig versjon i URL-banen." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Ugyldig versjon i vertsnavn." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Ugyldig versjon i søkeparameter." diff --git a/rest_framework/locale/nl/LC_MESSAGES/django.mo b/rest_framework/locale/nl/LC_MESSAGES/django.mo index 8b70ddbd68f7ef3424197c35c3c064fbb5abc4f5..8f9c2dcdede91e1447e0c78de83f7534d2561ac5 100644 GIT binary patch delta 3697 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 ay>0gIy4uicqX*4axrgP*{2XiM=zjnVslP`6 diff --git a/rest_framework/locale/nl/LC_MESSAGES/django.po b/rest_framework/locale/nl/LC_MESSAGES/django.po index b89af0606..6b9dd127b 100644 --- a/rest_framework/locale/nl/LC_MESSAGES/django.po +++ b/rest_framework/locale/nl/LC_MESSAGES/django.po @@ -3,14 +3,17 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# Hans van Luttikhuizen , 2016 # mikedingjan , 2015 +# mikedingjan , 2015 +# Hans van Luttikhuizen , 2016 msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Dutch (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/nl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -18,78 +21,78 @@ msgstr "" "Language: nl\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." -msgstr "Ongeldige basic header. Geen login gegevens opgegeven." +msgstr "Ongeldige basic header. Geen logingegevens opgegeven." -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." -msgstr "Ongeldige basic header. login gegevens kunnen geen spaties bevatten." +msgstr "Ongeldige basic header. logingegevens kunnen geen spaties bevatten." -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." -msgstr "Ongeldige basic header. login gegevens zijn niet correct base64 versleuteld." +msgstr "Ongeldige basic header. logingegevens zijn niet correct base64-versleuteld." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Ongeldige gebruikersnaam/wachtwoord." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "Gebruiker inactief of verwijderd." -#: authentication.py:173 -msgid "Invalid token header. No credentials provided." -msgstr "Ongeldige token header. Geen login gegevens opgegeven" - #: authentication.py:176 +msgid "Invalid token header. No credentials provided." +msgstr "Ongeldige token header. Geen logingegevens opgegeven" + +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "Ongeldige token header. Token kan geen spaties bevatten." -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." -msgstr "" +msgstr "Ongeldige token header. Token kan geen ongeldige karakters bevatten." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Ongeldige token." #: authtoken/apps.py:7 msgid "Auth Token" -msgstr "" +msgstr "Autorisatietoken" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" -msgstr "" +msgstr "Key" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" -msgstr "" +msgstr "Gebruiker" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" -msgstr "" +msgstr "Aangemaakt" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" -msgstr "" +msgstr "Token" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" -msgstr "" +msgstr "Tokens" #: authtoken/serializers.py:8 msgid "Username" -msgstr "" +msgstr "Gebruikersnaam" #: authtoken/serializers.py:9 msgid "Password" -msgstr "" +msgstr "Wachtwoord" #: authtoken/serializers.py:20 msgid "User account is disabled." -msgstr "Gebruikers account is inactief." +msgstr "Gebruikersaccount is gedeactiveerd." #: authtoken/serializers.py:23 msgid "Unable to log in with provided credentials." @@ -97,11 +100,11 @@ msgstr "Kan niet inloggen met opgegeven gegevens." #: authtoken/serializers.py:26 msgid "Must include \"username\" and \"password\"." -msgstr "Moet een \"gebruikersnaam\" en \"wachtwoord\" bevatten." +msgstr "Moet \"username\" en \"password\" bevatten." #: exceptions.py:49 msgid "A server error occurred." -msgstr "Er is een server fout opgetreden." +msgstr "Er is een serverfout opgetreden." #: exceptions.py:84 msgid "Malformed request." @@ -109,248 +112,233 @@ msgstr "Ongeldig samengestelde request." #: exceptions.py:89 msgid "Incorrect authentication credentials." -msgstr "Ongeldige authenticatie gegevens." +msgstr "Ongeldige authenticatiegegevens." #: exceptions.py:94 msgid "Authentication credentials were not provided." -msgstr "Authenticatie gegevens zijn niet opgegeven." +msgstr "Authenticatiegegevens zijn niet opgegeven." #: exceptions.py:99 msgid "You do not have permission to perform this action." -msgstr "Je hebt geen toegang om deze actie uit te voeren." +msgstr "Je hebt geen toestemming om deze actie uit te voeren." #: exceptions.py:104 views.py:81 msgid "Not found." msgstr "Niet gevonden." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "Methode \"{method}\" niet toegestaan." #: exceptions.py:120 msgid "Could not satisfy the request Accept header." -msgstr "Kan niet voldoen aan de opgegeven \"Accept\" header." +msgstr "Kan niet voldoen aan de opgegeven Accept header." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." -msgstr "Ongeldig media type \"{media_type}\" in aanvraag." +msgstr "Ongeldige media type \"{media_type}\" in aanvraag." #: exceptions.py:145 msgid "Request was throttled." msgstr "Aanvraag was verstikt." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." -msgstr "Dit veld is verplicht." +msgstr "Dit veld is vereist." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Dit veld mag niet leeg zijn." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." -msgstr "\"{input}\" is een ongeldige boolean waarde." +msgstr "\"{input}\" is een ongeldige booleanwaarde." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "Dit veld mag niet leeg zijn." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "Zorg ervoor dat dit veld niet meer dan {max_length} karakters bevat." -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "Zorg ervoor dat dit veld minimaal {min_length} karakters bevat." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Voer een geldig e-mailadres in." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." -msgstr "Deze waarde voldoet niet aan het benodigde formaat." +msgstr "Deze waarde voldoet niet aan het vereisde formaat." -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." -msgstr "Voer een geldige \"slug\" in bestaande uit letters, cijfers, underscore of streepjes." +msgstr "Voer een geldige \"slug\" in, bestaande uit letters, cijfers, lage streepjes of streepjes." -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Voer een geldige URL in." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." -msgstr "\"{value}\" in een ongeldige UUID." +msgstr "\"{value}\" is een ongeldige UUID." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." -msgstr "" +msgstr "Voer een geldig IPv4- of IPv6-adres in." -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "Een geldig getal is vereist." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." -msgstr "Zorg ervoor dat deze waarde minder of gelijk is aan {max_value}." +msgstr "Zorg ervoor dat deze waarde kleiner is dan of gelijk is aan {max_value}." -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." -msgstr "Zorg ervoor dat deze waarde groter of gelijk is aan {min_value}." +msgstr "Zorg ervoor dat deze waarde groter is dan of gelijk is aan {min_value}." -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." -msgstr "Tekstuele waarde is te lang." +msgstr "Tekstwaarde is te lang." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." -msgstr "Een geldig nummer is verplicht." +msgstr "Een geldig nummer is vereist." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." -msgstr "Zorg ervoor dat er niet meer dan {max_digits} getallen zijn in totaal." +msgstr "Zorg ervoor dat er in totaal niet meer dan {max_digits} cijfers zijn." -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." -msgstr "zorg ervoor dat er niet meer dan {max_decimal_places} getallen achter de komma zijn." +msgstr "Zorg ervoor dat er niet meer dan {max_decimal_places} cijfers achter de komma zijn." -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." -msgstr "Zorg ervoor dat er niet meer dan {max_whole_digits} getallen voor de komma zijn." +msgstr "Zorg ervoor dat er niet meer dan {max_whole_digits} cijfers voor de komma zijn." -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "Datetime heeft een ongeldig formaat, gebruik 1 van de volgende formaten: {format}." -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." -msgstr "Verwacht een datetime, in plaats kreeg een date." +msgstr "Verwachtte een datetime, maar kreeg een date." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." -msgstr "Date heeft het verkeerde formaat, gebruik 1 van de onderstaande formaten: {format}." +msgstr "Date heeft het verkeerde formaat, gebruik 1 van deze formaten: {format}." -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." -msgstr "Verwacht een date, in plaats kreeg een datetime" +msgstr "Verwachtte een date, maar kreeg een datetime." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "Time heeft het verkeerde formaat, gebruik 1 van onderstaande formaten: {format}." -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "Tijdsduur heeft een verkeerd formaat, gebruik 1 van onderstaande formaten: {format}." -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" is een ongeldige keuze." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." -msgstr "" +msgstr "Meer dan {count} items..." -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." -msgstr "Verwacht een lijst met items, kreeg een type \"{input_type}\" in plaats." +msgstr "Verwachtte een lijst met items, maar kreeg type \"{input_type}\"." -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." -msgstr "" +msgstr "Deze selectie mag niet leeg zijn." -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." -msgstr "" +msgstr "\"{input}\" is niet een geldig pad." -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." -msgstr "Er is geen bestand opgestuurd" +msgstr "Er is geen bestand opgestuurd." -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "De verstuurde data was geen bestand. Controleer de encoding type op het formulier." -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." -msgstr "Bestandsnaam kan niet vastgesteld worden." +msgstr "Bestandsnaam kon niet vastgesteld worden." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "Het verstuurde bestand is leeg." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." -msgstr "Zorg ervoor dat deze bestandsnaam minstens {max_length} karakters heeft (momenteel {length})." +msgstr "Zorg ervoor dat deze bestandsnaam hoogstens {max_length} karakters heeft (het heeft er {length})." -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." -msgstr "Upload een geldige afbeelding, de geüploade afbeelding is geen afbeelding of mogelijk corrupt geraakt," +msgstr "Upload een geldige afbeelding, de geüploade afbeelding is geen afbeelding of is beschadigd geraakt," -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." -msgstr "" +msgstr "Deze lijst mag niet leeg zijn." -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." -msgstr "Verwacht een dictionary van items, in plaats kreeg een type \"{input_type}\"." +msgstr "Verwachtte een dictionary van items, maar kreeg type \"{input_type}\"." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." -msgstr "" +msgstr "Waarde moet valide JSON zijn." -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" +msgstr "Verzenden" + +#: filters.py:336 +msgid "ascending" msgstr "" -#: pagination.py:189 +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." -msgstr "" +msgstr "Ongeldige pagina." -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "Ongeldige cursor." #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Ongeldige pk \"{pk_value}\" - object bestaat niet." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." -msgstr "Ongeldig type. Verwacht een pk waarde, ontving {data_type}." +msgstr "Ongeldig type. Verwacht een pk-waarde, ontving {data_type}." #: relations.py:240 msgid "Invalid hyperlink - No URL match." @@ -365,41 +353,38 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Ongeldige hyperlink - Object bestaat niet." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Ongeldig type. Verwacht een URL, ontving {data_type}." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "Object met {slug_name}={value} bestaat niet." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Ongeldige waarde." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Ongeldige data. Verwacht een dictionary, kreeg een {datatype}." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" -msgstr "" +msgstr "Filters" #: templates/rest_framework/filters/django_filter.html:2 #: templates/rest_framework/filters/django_filter_crispyforms.html:4 msgid "Field filters" -msgstr "" +msgstr "Veldfilters" #: templates/rest_framework/filters/ordering.html:3 msgid "Ordering" -msgstr "" +msgstr "Sorteer op" #: templates/rest_framework/filters/search.html:2 msgid "Search" -msgstr "" +msgstr "Zoek" #: templates/rest_framework/horizontal/radio.html:2 #: templates/rest_framework/inline/radio.html:2 @@ -413,27 +398,23 @@ msgstr "Geen" msgid "No items to select." msgstr "Geen items geselecteerd." -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Dit veld moet uniek zijn." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "De velden {field_names} moeten een unieke set zijn." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "Dit veld moet uniek zijn voor de \"{date_field}\" datum." -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "Dit veld moet uniek zijn voor de \"{date_field}\" maand." -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "Dit veld moet uniek zijn voor de \"{date_field}\" year." @@ -441,18 +422,22 @@ msgstr "Dit veld moet uniek zijn voor de \"{date_field}\" year." msgid "Invalid version in \"Accept\" header." msgstr "Ongeldige versie in \"Accept\" header." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." -msgstr "Ongeldige versie in URL pad." +msgstr "Ongeldige versie in URL-pad." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." -msgstr "Ongeldige versie in host naam." +msgstr "Ongeldige versie in hostnaam." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Ongeldige versie in query parameter." #: views.py:88 msgid "Permission denied." -msgstr "Toegang niet toegestaan." +msgstr "Toestemming geweigerd." diff --git a/rest_framework/locale/nn/LC_MESSAGES/django.mo b/rest_framework/locale/nn/LC_MESSAGES/django.mo index 43d080aa67cdb810998a30d1065253dcdb605a7d..a2c1e01f8dd4127b3f67ffe9bd98d580454c2ddc 100644 GIT binary patch delta 90 zcmeBS>0y~Lnaf<)&`811%*xPs;#@gL2+!2Y&_vq+2)KL_i%WDviW2jRa}rDPi>wqv hGV*g1ixr$RiZY8!GE)_7O7e3ZfH)Z{u0y~Lnafz$z)-=^!phiU;#@gL2+thI(>4GCE}z8W65WuZ#Ju91#FG3XD}{)} ivdq*X1^=Ry{KC>o1)GX|2+JWSGcUg^GjZb|0Y(6!&K*kt diff --git a/rest_framework/locale/nn/LC_MESSAGES/django.po b/rest_framework/locale/nn/LC_MESSAGES/django.po index 26a625b6d..bd4d690d2 100644 --- a/rest_framework/locale/nn/LC_MESSAGES/django.po +++ b/rest_framework/locale/nn/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Norwegian Nynorsk (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/nn/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: nn\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/no/LC_MESSAGES/django.mo b/rest_framework/locale/no/LC_MESSAGES/django.mo index 730768a272bbc0c161dc6435e89bf8d700c44b64..2c058c8c754c1bcbc5fdabc169a794b53b619f26 100644 GIT binary patch delta 90 zcmZo+Xwqv hGV*g1ixr$RiZY8!GE)_7O7e3ZfH)Z{u<;KEBLJ189Pt1E delta 90 zcmZo+X4GCE}z8W65WuZ#Ju91#FG3XD}{)} ivdq*X1^=Ry{KC>o1)GX|2+JWSGcUg^GjZb|4n_c+E*&!f diff --git a/rest_framework/locale/no/LC_MESSAGES/django.po b/rest_framework/locale/no/LC_MESSAGES/django.po index 4a3d609ca..1ddd6675f 100644 --- a/rest_framework/locale/no/LC_MESSAGES/django.po +++ b/rest_framework/locale/no/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Norwegian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/no/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: no\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/pl/LC_MESSAGES/django.mo b/rest_framework/locale/pl/LC_MESSAGES/django.mo index f228dfc7c72f6d5c1a7c86d8cd61ebe20b6f29e7..8af27437f01d85967413cde8c9ce0b58231f0f36 100644 GIT binary patch delta 895 zcmXZaPe_w-9LMob=@v~w(ag%)p33z{nYsB`ZI!uJ5@HT=VI&397FyZ^+q(7?lF*?b zJw^X-UHU5odFfK83cN?`)S*Llh$86H`->afUf<{YJkRgX_xpREdzpLT*7z${N?+DV z-I6Nnr6@LSkal7cx8fLX#Ov6GkC3n4^T#Wy;12wY33PaPH4bAhp21Ui53~3iPvX&5 z`8q^!w^cfUpK(8SZjv&10ng(zJc7-empzSFxPFK>aX2gysvmh&=dc5>VH}^LixoVK zYa`Mc9Etc+kf1DI%wveCLj4m(r;sEyG4YUos#a;Lhn=ncl zEuX|byo`hR+9$99Yqm*^cnsTd6zPg)aXa2e2j3xU(pR(=1?WcrJCUl&MZ2FxhL(p` zp*ge#FIMwQXyg082xFB>c>*i_8?=>w#4c3(!a(qQLuhX-o^axOQt|HjbW1qB zeA$zVyB%G8LWB8IIhHHtFO{b9Wv`fWaueQkzT{*kij$@CWWhO5_NM#zjaL&34_a#mhK`k8uQl;3y6@ z$X6f1qXy{$e#Vp7)yPe}iP!NZ4q;u>f5&i^@iN+n8-o&|hLA^f6+6+#2)@8>Ea4ff zZI-HWwAq&e1Q{j{;S>(weY}Vpcm-P=DT{MxiEQFtba|;4&tebWMccqScHnQU!&b^@ z^D*qlA|~;TPhbm{wUQMM<1rjZx}teJj7#X?J0vH4L2FTYn^b{aNLA&c-A^G)E00#8 z1+;|Mw&pj`*7v^?Y$w=B<9A~yDg(L5hjzcnN=^orNT6TMwV2E$|7croLhbE7|@q#;^isF@W=U z8t-8oKcKa68~v~ic}Ve;$96j{iv(8sw`iGv#4!HpSUnfmtf`8I!VxDDPekJn2I_-- z3}cQPNw__oVSZIfuQ(G*PkXnDId7&gop4fKX7XCr8Odb}&Z)c&rj8dF^xw#PleyJr I^^3Lt0BsO(kN^Mx diff --git a/rest_framework/locale/pl/LC_MESSAGES/django.po b/rest_framework/locale/pl/LC_MESSAGES/django.po index fdf4904bb..b8592e9b7 100644 --- a/rest_framework/locale/pl/LC_MESSAGES/django.po +++ b/rest_framework/locale/pl/LC_MESSAGES/django.po @@ -5,14 +5,14 @@ # Translators: # Janusz Harkot , 2015 # Piotr Jakimiak , 2015 -# Maciek Olko , 2015-2016 +# m_aciek , 2015-2016 msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-07 21:25+0000\n" -"Last-Translator: Maciek Olko \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Polish (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/pl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -20,40 +20,40 @@ msgstr "" "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:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "Niepoprawny podstawowy nagłówek. Brak danych uwierzytelniających." -#: authentication.py:74 +#: authentication.py:76 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:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "Niepoprawny podstawowy nagłówek. Niewłaściwe kodowanie base64 danych uwierzytelniających." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Niepoprawna nazwa użytkownika lub hasło." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "Użytkownik nieaktywny lub usunięty." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "Niepoprawny nagłówek tokena. Brak danych uwierzytelniających." -#: authentication.py:176 +#: authentication.py:179 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:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "Błędny nagłówek z tokenem. Token nie może zawierać błędnych znaków." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Niepoprawny token." @@ -61,23 +61,23 @@ msgstr "Niepoprawny token." msgid "Auth Token" msgstr "Token uwierzytelniający" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "Klucz" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "Użytkownik" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "Stworzono" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "Token" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "Tokeny" @@ -126,7 +126,6 @@ msgid "Not found." msgstr "Nie znaleziono." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "Niedozwolona metoda \"{method}\"." @@ -135,7 +134,6 @@ msgid "Could not satisfy the request Accept header." msgstr "Nie można zaspokoić nagłówka Accept żądania." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "Brak wsparcia dla żądanego typu danych \"{media_type}\"." @@ -143,214 +141,201 @@ msgstr "Brak wsparcia dla żądanego typu danych \"{media_type}\"." msgid "Request was throttled." msgstr "Żądanie zostało zdławione." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "To pole jest wymagane." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Pole nie może mieć wartości null." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" nie jest poprawną wartością logiczną." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "To pole nie może być puste." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 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:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "Upewnij się, że pole ma co najmniej {min_length} znaków." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Podaj poprawny adres e-mail." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "Ta wartość nie pasuje do wymaganego wzorca." -#: fields.py:730 +#: fields.py:735 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:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Wprowadź poprawny adres URL." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "\"{value}\" nie jest poprawnym UUID." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "Wprowadź poprawny adres IPv4 lub IPv6." -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "Wymagana poprawna liczba całkowita." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 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:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 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:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "Za długi ciąg znaków." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "Wymagana poprawna liczba." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 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:888 -#, python-brace-format +#: fields.py:894 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:889 -#, python-brace-format +#: fields.py:895 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:1004 -#, python-brace-format +#: fields.py:1025 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:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "Oczekiwano datę z czasem, otrzymano tylko datę." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 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:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "Oczekiwano daty a otrzymano datę z czasem." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 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:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "Czas trwania ma zły format. Użyj w zamian jednego z tych formatów: {format}." -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" nie jest poprawnym wyborem." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "Więcej niż {count} elementów..." -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "Oczekiwano listy elementów, a otrzymano dane typu \"{input_type}\"." -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "Zaznaczenie nie może być puste." -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "\"{input}\" nie jest poprawną ścieżką." -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "Nie przesłano pliku." -#: fields.py:1348 +#: fields.py:1359 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:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "Nie można określić nazwy pliku." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "Przesłany plik jest pusty." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 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:1399 +#: fields.py:1410 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:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "Lista nie może być pusta." -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "Oczekiwano słownika, ale otrzymano \"{input_type}\"." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "Wartość musi być poprawnym ciągiem znaków JSON" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "Wyślij" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "Niepoprawna strona." -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "Niepoprawny wskaźnik" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Błędny klucz główny \"{pk_value}\" - obiekt nie istnieje." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "Błędny typ danych. Oczekiwano wartość klucza głównego, otrzymano {data_type}." @@ -367,25 +352,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Błędny hyperlink - obiekt nie istnieje." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Błędny typ danych. Oczekiwano adresu URL, otrzymano {data_type}" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "Obiekt z polem {slug_name}={value} nie istnieje" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Niepoprawna wartość." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Niepoprawne dane. Oczekiwano słownika, otrzymano {datatype}." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "Filtry" @@ -415,27 +397,23 @@ msgstr "None" msgid "No items to select." msgstr "Nie wybrano wartości." -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Wartość dla tego pola musi być unikalna." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "Pola {field_names} muszą tworzyć unikalny zestaw." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 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:241 -#, python-brace-format +#: validators.py:260 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:254 -#, python-brace-format +#: validators.py:273 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}\"." @@ -443,15 +421,19 @@ msgstr "To pole musi mieć unikalną wartość dla konkretnego roku z pola \"{da msgid "Invalid version in \"Accept\" header." msgstr "Błędna wersja w nagłówku \"Accept\"." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "Błędna wersja w ścieżce URL." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Błędna wersja w nazwie hosta." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Błędna wersja w parametrach zapytania." diff --git a/rest_framework/locale/pt/LC_MESSAGES/django.mo b/rest_framework/locale/pt/LC_MESSAGES/django.mo index 9553b8f7ae0e46b54d4aed4f473515eb364357ea..9e3c7ab2f490b065535b03e05a2df1c16cb38e88 100644 GIT binary patch delta 90 zcmZo=X=Rx(naf<)&`811%*xPs;#@gL2+!2Y&_vq+2)KL_i%WDviW2jRa}rDPi>wqv hGV*g1ixr$RiZY8!GE)_7O7e3ZfH)Z{u<;KkBLJ3I9P|JH delta 90 zcmZo=X=Rx(nafz$z)-=^!phiU;#@gL2+thI(>4GCE}z8W65WuZ#Ju91#FG3XD}{)} ivdq*X1^=Ry{KC>o1)GX|2+JWSGcUg^GjZb|PDTKn;vF^s diff --git a/rest_framework/locale/pt/LC_MESSAGES/django.po b/rest_framework/locale/pt/LC_MESSAGES/django.po index c1217b2d6..9f1de1938 100644 --- a/rest_framework/locale/pt/LC_MESSAGES/django.po +++ b/rest_framework/locale/pt/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Portuguese (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/pt/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: pt\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/pt_BR/LC_MESSAGES/django.mo b/rest_framework/locale/pt_BR/LC_MESSAGES/django.mo index 20a41eb1f7bbc11f247fe8dd8755893c878296d4..1dd7287f36ab955d7ab8281100108cc84f22c8dd 100644 GIT binary patch delta 93 zcmez8|IdGenHZP3uAz~Fp_!GT@ni=vIY$WB)XLCA+W-i-d=iUGbVG^~^NMp4OY)1X k6hboca}$ddoHL3ti%T+76>LiKa~*&<87i=OzF52<03}-#c&9&-Qy diff --git a/rest_framework/locale/pt_BR/LC_MESSAGES/django.po b/rest_framework/locale/pt_BR/LC_MESSAGES/django.po index 8b55e9ed9..2c90f14ca 100644 --- a/rest_framework/locale/pt_BR/LC_MESSAGES/django.po +++ b/rest_framework/locale/pt_BR/LC_MESSAGES/django.po @@ -11,9 +11,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/pt_BR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -21,40 +21,40 @@ msgstr "" "Language: pt_BR\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "Cabeçalho básico inválido. Credenciais não fornecidas." -#: authentication.py:74 +#: authentication.py:76 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:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "Cabeçalho básico inválido. Credenciais codificadas em base64 incorretamente." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Usuário ou senha inválido." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "Usuário inativo ou removido." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "Cabeçalho de token inválido. Credenciais não fornecidas." -#: authentication.py:176 +#: authentication.py:179 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:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "Cabeçalho de token inválido. String de token não deve possuir caracteres inválidos." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Token inválido." @@ -62,23 +62,23 @@ msgstr "Token inválido." msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -127,7 +127,6 @@ msgid "Not found." msgstr "Não encontrado." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "Método \"{method}\" não é permitido." @@ -136,7 +135,6 @@ msgid "Could not satisfy the request Accept header." msgstr "Não foi possível satisfazer a requisição do cabeçalho Accept." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "Tipo de mídia \"{media_type}\" no pedido não é suportado." @@ -144,214 +142,201 @@ msgstr "Tipo de mídia \"{media_type}\" no pedido não é suportado." msgid "Request was throttled." msgstr "Pedido foi limitado." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "Este campo é obrigatório." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Este campo não pode ser nulo." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" não é um valor boleano válido." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "Este campo não pode ser em branco." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 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:671 -#, python-brace-format +#: fields.py:676 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:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Insira um endereço de email válido." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "Este valor não corresponde ao padrão exigido." -#: fields.py:730 +#: fields.py:735 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:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Entrar um URL válido." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "\"{value}\" não é um UUID válido." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "Informe um endereço IPv4 ou IPv6 válido." -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "Um número inteiro válido é exigido." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 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:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 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:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "Valor da string é muito grande." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "Um número válido é necessário." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 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:888 -#, python-brace-format +#: fields.py:894 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:889 -#, python-brace-format +#: fields.py:895 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:1004 -#, python-brace-format +#: fields.py:1025 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:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "Necessário uma data e hora mas recebeu uma data." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 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:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "Necessário uma data mas recebeu uma data e hora." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "Formato inválido para Tempo. Use um dos formatos a seguir: {format}." -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "Formato inválido para Duração. Use um dos formatos a seguir: {format}." -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" não é um escolha válido." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "Mais de {count} itens..." -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 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:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "Esta seleção não pode estar vazia." -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "\"{input}\" não é uma escolha válida para um caminho." -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "Nenhum arquivo foi submetido." -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "O dado submetido não é um arquivo. Certifique-se do tipo de codificação no formulário." -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "Nome do arquivo não pode ser determinado." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "O arquivo submetido está vázio." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "Certifique-se de que o nome do arquivo tem menos de {max_length} caracteres (tem {length})." -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "Fazer upload de uma imagem válida. O arquivo enviado não é um arquivo de imagem ou está corrompido." -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "Esta lista não pode estar vazia." -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "Esperado um dicionário de itens mas recebeu tipo \"{input_type}\"." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "Valor devo ser JSON válido." -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "Enviar" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "Cursor inválido" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Pk inválido \"{pk_value}\" - objeto não existe." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "Tipo incorreto. Esperado valor pk, recebeu {data_type}." @@ -368,25 +353,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Hyperlink inválido - Objeto não existe." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Tipo incorreto. Necessário string URL, recebeu {data_type}." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "Objeto com {slug_name}={value} não existe." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Valor inválido." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Dado inválido. Necessário um dicionário mas recebeu {datatype}." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "Filtra" @@ -416,27 +398,23 @@ msgstr "Nenhum(a/as)" msgid "No items to select." msgstr "Nenhum item para escholher." -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Esse campo deve ser único." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "Os campos {field_names} devem criar um set único." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "O campo \"{date_field}\" deve ser único para a data." -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "O campo \"{date_field}\" deve ser único para o mês." -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "O campo \"{date_field}\" deve ser único para o ano." @@ -444,15 +422,19 @@ msgstr "O campo \"{date_field}\" deve ser único para o ano." msgid "Invalid version in \"Accept\" header." msgstr "Versão inválida no cabeçalho \"Accept\"." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "Versão inválida no caminho de URL." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Versão inválida no hostname." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Versão inválida no parâmetro de query." diff --git a/rest_framework/locale/pt_PT/LC_MESSAGES/django.mo b/rest_framework/locale/pt_PT/LC_MESSAGES/django.mo index 82403c3cad8a9408909e20cbf276b8692e5b2af6..754e181547eb0e92090f8f04a285eb70141ebb66 100644 GIT binary patch delta 90 zcmbQnGL2=zWG-`ELn8%4Gb=;miF4%~Av{wnLlbQSAmH*zEH2RvDN4*M&PgoEFS1ey h$;i)5ELL#ND9S7@$xKzSDap@u0ODk*z{WpPi~yoc9VY+) delta 90 zcmbQnGL2=zWG-V}149Kv3oB!biF4%~Av|*+PulKW\n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Portuguese (Portugal) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/pt_PT/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: pt_PT\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/ro/LC_MESSAGES/django.mo b/rest_framework/locale/ro/LC_MESSAGES/django.mo index 77dc7d0f23fce6a90c9cf8c60fa31fd568372e46..5a113ab72d84c7bb47d55264e7b4a938aa00e46b 100644 GIT binary patch literal 10895 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, 2016 msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Romanian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/ro/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,441 +18,423 @@ msgstr "" "Language: ro\n" "Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." -msgstr "" +msgstr "Antet de bază invalid. Datele de autentificare nu au fost furnizate." -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." -msgstr "" +msgstr "Antet de bază invalid. Şirul de caractere cu datele de autentificare nu trebuie să conțină spații." -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." -msgstr "" +msgstr "Antet de bază invalid. Datele de autentificare nu au fost corect codificate în base64." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." -msgstr "" +msgstr "Nume utilizator / Parolă invalid(ă)." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." -msgstr "" - -#: authentication.py:173 -msgid "Invalid token header. No credentials provided." -msgstr "" +msgstr "Utilizator inactiv sau șters." #: authentication.py:176 -msgid "Invalid token header. Token string should not contain spaces." -msgstr "" +msgid "Invalid token header. No credentials provided." +msgstr "Antet token invalid. Datele de autentificare nu au fost furnizate." -#: authentication.py:182 +#: authentication.py:179 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "Antet token invalid. Şirul de caractere pentru token nu trebuie să conțină spații." + +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." -msgstr "" +msgstr "Antet token invalid. Şirul de caractere pentru token nu trebuie să conțină caractere nevalide." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." -msgstr "" +msgstr "Token nevalid." #: authtoken/apps.py:7 msgid "Auth Token" -msgstr "" +msgstr "Token de autentificare" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" -msgstr "" +msgstr "Cheie" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" -msgstr "" +msgstr "Utilizator" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" -msgstr "" +msgstr "Creat" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" -msgstr "" +msgstr "Token" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" -msgstr "" +msgstr "Tokenuri" #: authtoken/serializers.py:8 msgid "Username" -msgstr "" +msgstr "Nume de utilizator" #: authtoken/serializers.py:9 msgid "Password" -msgstr "" +msgstr "Parola" #: authtoken/serializers.py:20 msgid "User account is disabled." -msgstr "" +msgstr "Contul de utilizator este dezactivat." #: authtoken/serializers.py:23 msgid "Unable to log in with provided credentials." -msgstr "" +msgstr "Nu se poate conecta cu datele de conectare furnizate." #: authtoken/serializers.py:26 msgid "Must include \"username\" and \"password\"." -msgstr "" +msgstr "Trebuie să includă \"numele de utilizator\" și \"parola\"." #: exceptions.py:49 msgid "A server error occurred." -msgstr "" +msgstr "A apărut o eroare pe server." #: exceptions.py:84 msgid "Malformed request." -msgstr "" +msgstr "Cerere incorectă." #: exceptions.py:89 msgid "Incorrect authentication credentials." -msgstr "" +msgstr "Date de autentificare incorecte." #: exceptions.py:94 msgid "Authentication credentials were not provided." -msgstr "" +msgstr "Datele de autentificare nu au fost furnizate." #: exceptions.py:99 msgid "You do not have permission to perform this action." -msgstr "" +msgstr "Nu aveți permisiunea de a efectua această acțiune." #: exceptions.py:104 views.py:81 msgid "Not found." -msgstr "" +msgstr "Nu a fost găsit(ă)." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." -msgstr "" +msgstr "Metoda \"{method}\" nu este permisa." #: exceptions.py:120 msgid "Could not satisfy the request Accept header." -msgstr "" +msgstr "Antetul Accept al cererii nu a putut fi îndeplinit." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." -msgstr "" +msgstr "Cererea conține tipul media neacceptat \"{media_type}\"" #: exceptions.py:145 msgid "Request was throttled." -msgstr "" +msgstr "Cererea a fost gâtuită." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." -msgstr "" +msgstr "Acest câmp este obligatoriu." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." -msgstr "" +msgstr "Acest câmp nu poate fi nul." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." -msgstr "" +msgstr "\"{input}\" nu este un boolean valid." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." -msgstr "" +msgstr "Acest câmp nu poate fi gol." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." -msgstr "" +msgstr "Asigurați-vă că acest câmp nu are mai mult de {max_length} caractere." -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." -msgstr "" +msgstr "Asigurați-vă că acest câmp are cel puțin{min_length} caractere." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." -msgstr "" +msgstr "Introduceți o adresă de email validă." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." -msgstr "" +msgstr "Această valoare nu se potrivește cu şablonul cerut." -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." -msgstr "" +msgstr "Introduceți un \"slug\" valid format din litere, numere, caractere de subliniere sau cratime." -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." -msgstr "" +msgstr "Introduceți un URL valid." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." -msgstr "" +msgstr "\"{value}\" nu este un UUID valid." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." -msgstr "" +msgstr "Introduceți o adresă IPv4 sau IPv6 validă." -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." -msgstr "" +msgstr "Este necesar un întreg valid." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." -msgstr "" +msgstr "Asigurați-vă că această valoare este mai mică sau egală cu {max_value}." -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." -msgstr "" +msgstr "Asigurați-vă că această valoare este mai mare sau egală cu {min_value}." -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." -msgstr "" +msgstr "Valoare șir de caractere prea mare." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." -msgstr "" +msgstr "Este necesar un număr valid." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." -msgstr "" +msgstr "Asigurați-vă că nu există mai mult de {max_digits} cifre în total." -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." -msgstr "" +msgstr "Asigurați-vă că nu există mai mult de {max_decimal_places} zecimale." -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." -msgstr "" +msgstr "Asigurați-vă că nu există mai mult de {max_whole_digits} cifre înainte de punctul zecimal." -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." -msgstr "" +msgstr "Câmpul datetime are format greșit. Utilizați unul dintre aceste formate în loc: {format}." -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." -msgstr "" +msgstr "Se aștepta un câmp datetime, dar s-a primit o dată." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." -msgstr "" +msgstr "Data are formatul greșit. Utilizați unul dintre aceste formate în loc: {format}." -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." -msgstr "" +msgstr "Se aștepta o dată, dar s-a primit un câmp datetime." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." -msgstr "" +msgstr "Timpul are formatul greșit. Utilizați unul dintre aceste formate în loc: {format}." -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." -msgstr "" +msgstr "Durata are formatul greșit. Utilizați unul dintre aceste formate în loc: {format}." -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." -msgstr "" +msgstr "\"{input}\" nu este o opțiune validă." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." -msgstr "" +msgstr "Mai mult de {count} articole ..." -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." -msgstr "" +msgstr "Se aștepta o listă de elemente, dar s-a primit tip \"{input_type}\"." -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." -msgstr "" +msgstr "Această selecție nu poate fi goală." -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." -msgstr "" +msgstr "\"{input}\" nu este o cale validă." -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." -msgstr "" +msgstr "Nici un fișier nu a fost sumis." -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." -msgstr "" +msgstr "Datele prezentate nu sunt un fișier. Verificați tipul de codificare de pe formular." -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." -msgstr "" +msgstr "Numele fișierului nu a putut fi determinat." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." -msgstr "" +msgstr "Fișierul sumis este gol." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." -msgstr "" +msgstr "Asigurați-vă că acest nume de fișier are cel mult {max_length} caractere (momentan are {length})." -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." -msgstr "" +msgstr "Încărcați o imagine validă. Fișierul încărcat a fost fie nu o imagine sau o imagine coruptă." -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." -msgstr "" +msgstr "Această listă nu poate fi goală." -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." -msgstr "" +msgstr "Se aștepta un dicționar de obiecte, dar s-a primit tipul \"{input_type}\"." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." -msgstr "" +msgstr "Valoarea trebuie să fie JSON valid." -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" +msgstr "Sumiteţi" + +#: filters.py:336 +msgid "ascending" msgstr "" -#: pagination.py:189 +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." -msgstr "" +msgstr "Pagină nevalidă." -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" -msgstr "" +msgstr "Cursor nevalid" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." -msgstr "" +msgstr "Pk \"{pk_value}\" nevalid - obiectul nu există." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." -msgstr "" +msgstr "Tip incorect. Se aștepta un pk, dar s-a primit \"{data_type}\"." #: relations.py:240 msgid "Invalid hyperlink - No URL match." -msgstr "" +msgstr "Hyperlink nevalid - Nici un URL nu se potrivește." #: relations.py:241 msgid "Invalid hyperlink - Incorrect URL match." -msgstr "" +msgstr "Hyperlink nevalid - Potrivire URL incorectă." #: relations.py:242 msgid "Invalid hyperlink - Object does not exist." -msgstr "" +msgstr "Hyperlink nevalid - Obiectul nu există." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." -msgstr "" +msgstr "Tip incorect. Se aștepta un URL, dar s-a primit \"{data_type}\"." + +#: relations.py:401 +msgid "Object with {slug_name}={value} does not exist." +msgstr "Obiectul cu {slug_name}={value} nu există." #: relations.py:402 -#, python-brace-format -msgid "Object with {slug_name}={value} does not exist." -msgstr "" - -#: relations.py:403 msgid "Invalid value." -msgstr "" +msgstr "Valoare nevalidă." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." -msgstr "" +msgstr "Date nevalide. Se aștepta un dicționar de obiecte, dar s-a primit \"{datatype}\"." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" -msgstr "" +msgstr "Filtre" #: templates/rest_framework/filters/django_filter.html:2 #: templates/rest_framework/filters/django_filter_crispyforms.html:4 msgid "Field filters" -msgstr "" +msgstr "Filtre câmpuri" #: templates/rest_framework/filters/ordering.html:3 msgid "Ordering" -msgstr "" +msgstr "Ordonare" #: templates/rest_framework/filters/search.html:2 msgid "Search" -msgstr "" +msgstr "Căutare" #: templates/rest_framework/horizontal/radio.html:2 #: templates/rest_framework/inline/radio.html:2 #: templates/rest_framework/vertical/radio.html:2 msgid "None" -msgstr "" +msgstr "Nici unul/una" #: templates/rest_framework/horizontal/select_multiple.html:2 #: templates/rest_framework/inline/select_multiple.html:2 #: templates/rest_framework/vertical/select_multiple.html:2 msgid "No items to select." -msgstr "" +msgstr "Nu există elemente pentru a fi selectate." -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." -msgstr "" +msgstr "Acest câmp trebuie să fie unic." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." -msgstr "" +msgstr "Câmpurile {field_names} trebuie să formeze un set unic." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." -msgstr "" +msgstr "Acest câmp trebuie să fie unic pentru data \"{date_field}\"." -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." -msgstr "" +msgstr "Acest câmp trebuie să fie unic pentru luna \"{date_field}\"." -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." -msgstr "" +msgstr "Acest câmp trebuie să fie unic pentru anul \"{date_field}\"." #: versioning.py:42 msgid "Invalid version in \"Accept\" header." -msgstr "" +msgstr "Versiune nevalidă în antetul \"Accept\"." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." +msgstr "Versiune nevalidă în calea URL." + +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." msgstr "" -#: versioning.py:144 +#: versioning.py:147 msgid "Invalid version in hostname." -msgstr "" +msgstr "Versiune nevalidă în numele de gazdă." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." -msgstr "" +msgstr "Versiune nevalid în parametrul de interogare." #: views.py:88 msgid "Permission denied." -msgstr "" +msgstr "Permisiune refuzată." diff --git a/rest_framework/locale/ru/LC_MESSAGES/django.mo b/rest_framework/locale/ru/LC_MESSAGES/django.mo index ac67f09a3be91baa792881c22e91fe1d9dac66f8..be0620a3328bfbfca68e546d34b94bbe152402ca 100644 GIT binary patch delta 3379 zcmZYAe^8Xi8Nl%c6a)kUgkRCpkhQLh=NiRlh}iM6HbpiygPKP(*kNW z22xtrG-)%b)&7_^GjWIq6I8%zC(}%I%=>00GiftT>!g{ z_WkU>_ulTayU%<4Y0uxg`aa5=_L8D>QfE_ZQj|K4O{rWcJu{Rl#e=BPU>T0$9Q-{N z<6V^R=cOw(3zuOAZo>*}$EA1z)9_8)s8pZ2Ok*=QzDn4@w;Jicfa~!+Y{&derB>m7 zd;~AxHv9M~ZN?W-w(vt-j2`ys0Pe;^ zcpcB+YN9@hH<7VbBM-~>11PW86?_ao!CLfivmQH8em5@Q3<>RX8Z1VAiA(VzGDKd( zMwGKUgc9*ZT#N5x1?KUv9MyUx?^H8BiXF(})p?wWZzHFq-a|>czhVZ?V~}OCH5D{? zW7N}l7<=#u{1gdZZ6q2=mH^61&Y}c(lgl3bCq9L3WSaCxa1EyOu1UZRC=>UiBJ{;%S%d58AH%u$Yutbz;|?sVQ7Q>#3<)Hk znn}G^WUO>58zFJTx5I64VGOrOQpvGMk%WAlDv2sZRcyU(Ofwd^&~Z!DrFXRx2&I6)h#Jpi>Q)Ba*k3Sph|Lmo!UuFqe>~I=DMwa zU#8J~hAKzdM3tOrrq)rtR8GbH%>Xx{cVeF;h?I3yNxJ_m^>hlU^Ak6p##E}jZc^mT zXS=QVn<7ne(fxgp_G)S?b(?f3Tc|m1EB-4=)3l^En%>l+X+7R(ml;g`)(pKXbifL# z4W>S`#LP|Gkk$~kyiv<%_N5KZ*wh>LM*X3n>B+5+eU=tX&fmJ@P>l|SHP&jc&lk2L zrZM9$1)Kd=z^9+}2clLuqBh5kt;j4$PVqixnRQvOquo?+u#@_1XzcJqV0VgIU% z>gBq+wyt`Gr>eTDDq}}WMXPnlFYBn-#Ie-rI!wU&W5fq>+oB8Z8X%qp4yqX5&K^Liez)Npr+)SbKE&*kJ%I29<`^m zbHX06FWXn`5p%uZlkyAplzoLyC$&A2cyN-($9Zg=4@R7mIIhf{IoiBkcs%nw&%eb$ zBle`3DDs$tMemrYq6OF-TT^^9Wz{HyU3LZ-;}WCD#8;gmd(s)SFEfQb>h#-V&Vb}s zjb`Yp_PCpCX78+pvEtchlFEN7cby^UnA7hJYWo@!UX?MG{a>G$>N&-+Up;&`Wx+Lu z9Ff`LGZFK!mH^z_lLT>+wT{d$Fx%&*#%`C^BqdFnW?hu+4Dgi+zQ_d5pvj#-D>gDO zGdb;tbid=AGM5*$R=nZPJs}&!TBhtv+J4i0e#m*=9@EY#KAE_e%_hCHj9$pA5X%KWUXz4@Gc;0((#*;iPC+m*BEmzg<@ab=%ldD3Nv zlzo;bWTD6HYnoFTv1Kd9oYNAnxm#9~&WL<{z`kNU3zz5qh$Y}nej>}I%par&8i!Aj3mE&ibJezqw4e$yF@r7r1DGVgkd%n8rX^jGW&XW0DM hQ<9_Xm+eX7l{In>rfE@$xn7i?I-%Y7A!}*dzW}AZ2VDRF delta 1917 zcmZA1Uu;uV9Ki9@vaMTj8~fME){R@Yf&0_0TL&v;vOkK04P;x${xHaBT$O2A-P&!A z8S5U9WC_uDV326UglM8fiCm%)@j(btiP?)jmqM_%9|HzrWl1pugnwbI!eO z?>WEUx##!8-wr3gDa<&fh#s1o7PTuC!7CYj5Pexnd9WW1jAJF9#Cy?1`Q7JOjz6Lo zGaX7bU;w-D5I&6Oup4h66lH{QiU>~W?h za1g5)A5Hrz%Ku!&&GUFhcdDkL2oO5aGJtn3U*s?Xylyn!yf zg9hg0E5)Kz9X8@-l=~4Z#b;0^b`mA!*HNC6yDoLV4wG`Bi;mQ847cC{N~$lW$G1^d z;3-Hws12p2QLM)c*o0r>2<9^T_4s(&MQmpLG7hl6R&a>1r-c0Pq%&WVs>xMsWPBH; zBwm&y6FGuA@O2!;pKu1dS>{pv2$o3@8TBx1KZKZmwRy% z`Kh{$4R{MBvq@)7YDQhy#)Szi#y4;~euz@rzfelk!1U+v0FL07Sc^V(sBF?ngBZn+@fz;LW9+yNT){rfXE`B`$-&>o`o(Eg1IeI$FAzL_1@`*Vi$2RTdTl5^6o-MKf-yu4z&_OCmk9R=6) zNu)lef}ucAca~OmriQHy>-~gtcQ~H#?wbxz#-rgxY&v8Nhi4}u)5hNE@z~VN zywUb_EY<6XPE5vTC&K1yr2)IHD*H~aR(td#<%@d2U8^(QpX$ZR3UjpLoi%#FQ*1uw zdC6vas`4}RCpCp;bW^KM8#Q(2q1qR0`p>#PJy(BL_c!$GUmBXV&+9j*ykFS0>Ho?+ P*YvDShXPA_V@uyZH5&6| diff --git a/rest_framework/locale/ru/LC_MESSAGES/django.po b/rest_framework/locale/ru/LC_MESSAGES/django.po index 980a9e5b4..b73270906 100644 --- a/rest_framework/locale/ru/LC_MESSAGES/django.po +++ b/rest_framework/locale/ru/LC_MESSAGES/django.po @@ -5,14 +5,15 @@ # Translators: # Kirill Tarasenko, 2015 # koodjo , 2015 -# Mikhail Dmitriev , 2015 +# Mike TUMS , 2015 +# Sergei Sinitsyn , 2016 msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Russian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -20,74 +21,74 @@ msgstr "" "Language: ru\n" "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "Недопустимый заголовок. Не предоставлены учетные данные." -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "Недопустимый заголовок. Учетные данные не должны содержать пробелов." -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "Недопустимый заголовок. Учетные данные некорректно закодированны в base64." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Недопустимые имя пользователя или пароль." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "Пользователь неактивен или удален." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "Недопустимый заголовок токена. Не предоставлены учетные данные." -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "Недопустимый заголовок токена. Токен не должен содержать пробелов." -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." -msgstr "" +msgstr "Недопустимый заголовок токена. Токен не должен содержать недопустимые символы." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Недопустимый токен." #: authtoken/apps.py:7 msgid "Auth Token" -msgstr "" +msgstr "Токен аутентификации" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" -msgstr "" +msgstr "Ключ" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" -msgstr "" +msgstr "Пользователь" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" -msgstr "" +msgstr "Создан" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" -msgstr "" +msgstr "Токен" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" -msgstr "" +msgstr "Токены" #: authtoken/serializers.py:8 msgid "Username" -msgstr "" +msgstr "Имя пользователя" #: authtoken/serializers.py:9 msgid "Password" -msgstr "" +msgstr "Пароль" #: authtoken/serializers.py:20 msgid "User account is disabled." @@ -126,7 +127,6 @@ msgid "Not found." msgstr "Не найдено." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "Метод \"{method}\" не разрешен." @@ -135,7 +135,6 @@ msgid "Could not satisfy the request Accept header." msgstr "Невозможно удовлетворить \"Accept\" заголовок запроса." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "Неподдерживаемый тип данных \"{media_type}\" в запросе." @@ -143,214 +142,201 @@ msgstr "Неподдерживаемый тип данных \"{media_type}\" в msgid "Request was throttled." msgstr "Запрос был проигнорирован." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "Это поле обязательно." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Это поле не может быть null." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" не является корректным булевым значением." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "Это поле не может быть пустым." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "Убедитесь что в этом поле не больше {max_length} символов." -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "Убедитесь что в этом поле как минимум {min_length} символов." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Введите корректный адрес электронной почты." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "Значение не соответствует требуемому паттерну." -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "Введите корректный \"slug\", состоящий из букв, цифр, знаков подчеркивания или дефисов." -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Введите корректный URL." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "\"{value}\" не является корректным UUID." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." -msgstr "" +msgstr "Введите действительный адрес IPv4 или IPv6." -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "Требуется целочисленное значение." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "Убедитесь что значение меньше или равно {max_value}." -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "Убедитесь что значение больше или равно {min_value}." -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "Слишком длинное значение." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "Требуется численное значение." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "Убедитесь что в числе не больше {max_digits} знаков." -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "Убедитесь что в числе не больше {max_decimal_places} знаков в дробной части." -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "Убедитесь что в цисле не больше {max_whole_digits} знаков в целой части." -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "Неправильный формат datetime. Используйте один из этих форматов: {format}." -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "Ожидался datetime, но был получен date." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "Неправильный формат date. Используйте один из этих форматов: {format}." -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "Ожидался date, но был получен datetime." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "Неправильный формат времени. Используйте один из этих форматов: {format}." -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." -msgstr "" +msgstr "Неправильный формат. Используйте один из этих форматов: {format}." -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" не является корректным значением." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." -msgstr "" +msgstr "Элементов больше чем {count}" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "Ожидался list со значениями, но был получен \"{input_type}\"." -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." -msgstr "" +msgstr "Выбор не может быть пустым." -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." -msgstr "" +msgstr "\"{input}\" не является корректным путем до файла" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "Не был загружен файл." -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "Загруженный файл не является корректным файлом. " -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "Невозможно определить имя файла." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "Загруженный файл пуст." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "Убедитесь что имя файла меньше {max_length} символов (сейчас {length})." -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "Загрузите корректное изображение. Загруженный файл не является изображением, либо является испорченным." -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." -msgstr "" +msgstr "Этот список не может быть пустым." -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "Ожидался словарь со значениями, но был получен \"{input_type}\"." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." -msgstr "" +msgstr "Значение должно быть правильным JSON." -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" +msgstr "Отправить" + +#: filters.py:336 +msgid "ascending" msgstr "" -#: pagination.py:189 +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." -msgstr "" +msgstr "Неправильная страница" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "Не корректный курсор" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Недопустимый первичный ключ \"{pk_value}\" - объект не существует." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "Некорректный тип. Ожилалось значение первичного ключа, получен {data_type}." @@ -367,75 +353,68 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Недопустимая ссылка - объект не существует." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Некорректный тип. Ожидался URL, получен {data_type}." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "Объект с {slug_name}={value} не существует." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Недопустимое значение." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Недопустимые данные. Ожидался dictionary, но был получен {datatype}." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" -msgstr "" +msgstr "Фильтры" #: templates/rest_framework/filters/django_filter.html:2 #: templates/rest_framework/filters/django_filter_crispyforms.html:4 msgid "Field filters" -msgstr "" +msgstr "Фильтры полей" #: templates/rest_framework/filters/ordering.html:3 msgid "Ordering" -msgstr "" +msgstr "Порядок сортировки" #: templates/rest_framework/filters/search.html:2 msgid "Search" -msgstr "" +msgstr "Поиск" #: templates/rest_framework/horizontal/radio.html:2 #: templates/rest_framework/inline/radio.html:2 #: templates/rest_framework/vertical/radio.html:2 msgid "None" -msgstr "" +msgstr "Ничего" #: templates/rest_framework/horizontal/select_multiple.html:2 #: templates/rest_framework/inline/select_multiple.html:2 #: templates/rest_framework/vertical/select_multiple.html:2 msgid "No items to select." -msgstr "" +msgstr "Нет элементов для выбора" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Это поле должно быть уникально." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "Поля {field_names} должны производить массив с уникальными значениями." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "Это поле должно быть уникально для даты \"{date_field}\"." -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "Это поле должно быть уникально для месяца \"{date_field}\"." -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "Это поле должно быть уникально для года \"{date_field}\"." @@ -443,18 +422,22 @@ msgstr "Это поле должно быть уникально для года msgid "Invalid version in \"Accept\" header." msgstr "Недопустимая версия в заголовке \"Accept\"." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "Недопустимая версия в пути URL." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Недопустимая версия в имени хоста." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Недопустимая версия в параметре запроса." #: views.py:88 msgid "Permission denied." -msgstr "" +msgstr "Доступ запрещен" diff --git a/rest_framework/locale/sk/LC_MESSAGES/django.mo b/rest_framework/locale/sk/LC_MESSAGES/django.mo index 83237889833cddac18370ba584b677f8c4fbdcf3..dda693e32c59738c5bcd1218feb7157b3a5a6078 100644 GIT binary patch delta 118 zcmX@?dDwG!EqWfqrYrYhK!+tSB(nej delta 118 zcmX@?dDwGmAfdN;5ziv=!S!Qu&ex9yNVo9o%f{}rtnXUmu zojF*Y0Z^S!VsVLXNKs;5aZX}Mevy?zL}FQHYLS9}QA&PcX{CZqMLvY(kdv90UzVA; Jd7`k9AOQYSB=rCQ diff --git a/rest_framework/locale/sk/LC_MESSAGES/django.po b/rest_framework/locale/sk/LC_MESSAGES/django.po index 208c063ac..1c22d09f0 100644 --- a/rest_framework/locale/sk/LC_MESSAGES/django.po +++ b/rest_framework/locale/sk/LC_MESSAGES/django.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Slovak (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/sk/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -18,40 +18,40 @@ msgstr "" "Language: sk\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "Nesprávna hlavička. Neboli poskytnuté prihlasovacie údaje." -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "Nesprávna hlavička. Prihlasovacie údaje nesmú obsahovať medzery." -#: authentication.py:80 +#: authentication.py:82 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:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Nesprávne prihlasovacie údaje." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "Daný používateľ je neaktívny, alebo zmazaný." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "Nesprávna token hlavička. Neboli poskytnuté prihlasovacie údaje." -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "Nesprávna token hlavička. Token hlavička nesmie obsahovať medzery." -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Nesprávny token." @@ -59,23 +59,23 @@ msgstr "Nesprávny token." msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -124,7 +124,6 @@ msgid "Not found." msgstr "Nebolo nájdené." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "Metóda \"{method}\" nie je povolená." @@ -133,7 +132,6 @@ msgid "Could not satisfy the request Accept header." msgstr "Nie je možné vyhovieť požiadavku v hlavičke \"Accept\"." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "Požiadavok obsahuje nepodporovaný media type: \"{media_type}\"." @@ -141,214 +139,201 @@ msgstr "Požiadavok obsahuje nepodporovaný media type: \"{media_type}\"." msgid "Request was throttled." msgstr "Požiadavok bol obmedzený, z dôvodu prekročenia limitu." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "Toto pole je povinné." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Toto pole nemôže byť nulové." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" je validný boolean." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "Toto pole nemože byť prázdne." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "Uistite sa, že toto pole nemá viac ako {max_length} znakov." -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "Uistite sa, že toto pole má viac ako {min_length} znakov." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Vložte správnu emailovú adresu." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "Toto pole nezodpovedá požadovanému formátu." -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "Zadajte platný \"slug\", ktorý obsahuje len malé písmená, čísla, spojovník alebopodtržítko." -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Zadajte platnú URL adresu." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "\"{value}\" nie je platné UUID." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "Je vyžadované celé číslo." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "Uistite sa, že hodnota je menšia alebo rovná {max_value}." -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "Uistite sa, že hodnota je väčšia alebo rovná {min_value}." -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "Zadaný textový reťazec je príliš dlhý." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "Je vyžadované číslo." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "Uistite sa, že hodnota neobsahuje viac ako {max_digits} cifier." -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "Uistite sa, že hodnota neobsahuje viac ako {max_decimal_places} desatinných miest." -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "Uistite sa, že hodnota neobsahuje viac ako {max_whole_digits} cifier pred desatinnou čiarkou." -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "Nesprávny formát dátumu a času. Prosím použite jeden z nasledujúcich formátov: {format}." -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "Vložený len dátum - date namiesto dátumu a času - datetime." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "Nesprávny formát dátumu. Prosím použite jeden z nasledujúcich formátov: {format}." -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "Vložený dátum a čas - datetime namiesto jednoduchého dátumu - date." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "Nesprávny formát času. Prosím použite jeden z nasledujúcich formátov: {format}." -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" je nesprávny výber z daných možností." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "Bol očakávaný zoznam položiek, no namiesto toho bol nájdený \"{input_type}\"." -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "Nebol odoslaný žiadny súbor." -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "Odoslané dáta neobsahujú súbor. Prosím skontrolujte kódovanie - encoding type daného formuláru." -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "Nebolo možné určiť meno súboru." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "Odoslaný súbor je prázdny." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "Uistite sa, že meno súboru neobsahuje viac ako {max_length} znakov. (V skutočnosti ich má {length})." -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "Uploadujte prosím obrázok. Súbor, ktorý ste uploadovali buď nie je obrázok, alebo daný obrázok je poškodený." -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "Bol očakávaný slovník položiek, no namiesto toho bol nájdený \"{input_type}\"." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "Nesprávny kurzor." #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Nesprávny primárny kľúč \"{pk_value}\" - objekt s daným primárnym kľúčom neexistuje." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "Nesprávny typ. Bol prijatý {data_type} namiesto primárneho kľúča." @@ -365,25 +350,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Nesprávny hypertextový odkaz - požadovný objekt neexistuje." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Nesprávny typ {data_type}. Požadovaný typ: hypertextový odkaz." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "Objekt, ktorého atribút \"{slug_name}\" je \"{value}\" neexistuje." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Nesprávna hodnota." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Bol očakávaný slovník položiek, no namiesto toho bol nájdený \"{datatype}\"." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -413,27 +395,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Táto položka musí byť unikátna." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "Dané položky: {field_names} musia tvoriť musia spolu tvoriť unikátnu množinu." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "Položka musí byť pre špecifický deň \"{date_field}\" unikátna." -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "Položka musí byť pre mesiac \"{date_field}\" unikátna." -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "Položka musí byť pre rok \"{date_field}\" unikátna." @@ -441,15 +419,19 @@ msgstr "Položka musí byť pre rok \"{date_field}\" unikátna." msgid "Invalid version in \"Accept\" header." msgstr "Nesprávna verzia v \"Accept\" hlavičke." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "Nesprávna verzia v URL adrese." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Nesprávna verzia v \"hostname\"." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Nesprávna verzia v parametri požiadavku." diff --git a/rest_framework/locale/sv/LC_MESSAGES/django.mo b/rest_framework/locale/sv/LC_MESSAGES/django.mo index d560de6e148ec09886153f5ccc95854ea689da9d..cbecec44db41c28528e50379a8db597b0f7bb909 100644 GIT binary patch delta 2471 zcmY+^e@sv_(-_c_mx z?{hAz}TiHam+MzGn>g)*3GOff!*7RGTAp29->73bhTsQ>4r88Z*d za5^?&DR$s;d=UfqF>Wv>Wt?U6}UW;hv6>N@Al&Z_%3QCFW^%A3p04WDabOW97|9g?7#={FxKLas17r;jhTyu zsP-DvOtzqw{($F*cYh3%xPKnC0u!h$yN2pVH)AC@k4io^poN{7i_c&(9z}M+ypCmf z9NBJj(eoGNW3F*gKetfnGlNcMV-Bjl6w5J)+TyNU_Fp4E>HY8=sy&HHqPtjyb7r}F zyA9PojGDn$sNY>i3#U*6%+GU@%QRvN?Ge-fj^Z+W6N7jrkM-Y4S*b_lm(JHCun_&e6&Y?iSVJ8&b8qyBdTJ1}@3`G&D0R5X&0 zkiN|~r~yrSrjs$+t2G$H=a3U;&Y%W5fkpTeY64TJV_L?|X55Rr@DzqH%Oa7n73*~V zZ7SN6cTmUV0xrQR)G;h7c9*ymL$r5eBfgFGcpW()reuM8ysA;{PSlq4p$2#q8JqbK z^}Gr6^L%rg3VCCE{F=T@K58YZQ3Gj1eg7<~qhntC6I61YLmkH-Q0MwL3}E1XW704a zHRA%T!6lfCJvg7|n_enf%0sBEd7V16GNlYVDw6)tce=Vs>5~0^+9kGK@Q6?+-o*=r30HLBp&30<{ zF3D82UFn359=m29BDAG8Q9-m5D%xJ%JgPCI$YX5WJjV0 z?BRi-8moQZK))Te8uktKMu&UD*1F+={t(ga-AEkI&zY7otTD$&=j`*h_1S}VPa>_r z7f7+9y*+lqXI=F9TMl;jg%2by7H9g>n$Lb34M#NQD+`K}{y*kBi@x`TlO}DjMv!=Jr>6Y7dbI#41&bzib?bgh-_De7?TyQi!+NK#4 zM_|w&fx@>eB+}T*A_(z_S{hM_L{M3fIWi%^e%QNHANKw}KA*4m{qcE! z-k+}@Z=dT+UoOde#b~3%y~J-hW@$XLfCKF#zgZQ|Vi14ATD*nJuzI1{-55h%KZ^I@ z)7Xd(HsELY0A9n**i?`;Z3pP|aN<*pqQB5=5Vv3}9>aG05#w08$Si`7q5l0S_Tu+g zgUjzQE5T0G3k{;qPkO$Jy5DJR(NzDYb0;SnIWQ?}N6oMk^`IoS;S6rVpRp4wi_Aip zK>qA72mS6C*5O4|=6s9I?!y`kV;_bvja!xK_vviJe~@cz-4e5<*o_+RLd_tBnvvsq z(mVe(YQ}${mL#v(y}kxlG48+sK7m1e4omS3Oy|)#MThL#hj>4Jjx3X1N8R`)>IOv& z^05}nvB4X6VVH3owUp1IUi2*LcfWY!8>rd{@bq=Krj-0^4U^u9H&HXci2C857{tYQ zxi1z*711tSjnmkK-{J`R%FHTohvz=eL%4?X@8Xv}vrDLnzEw{CmD(S8&9!&~H4ahI zdXaw5J)SRO1Lvo4J)Xy{Sjbc3IDi@-N7cx$7{w4b8^9E5=4Y`3Z>H&NrxRhiUHB5} z!tZbp%gCeFa2!d7y^LI9@1S1f3(wzCsjsYbA2^0=2zwi~WT$XB&fqdF0Qoa?s5E)$W6!|Jb9{TYD zYGRi$qWyo3j&?ykuR|6sgi2W)Rb2h3B|3<#id{hd>^cW6O<;xlz39MAjCW%cXK*84 zMg6{pkBaQ^d=hJUz8#{Ylzo5^yoD>VmBUWliOS3;$i3_m2GPgURZP|B!~a{e*U>B~ z^z28e8+!M*Ua zH!(TYI53geJ2{@1N=-z9!-@ULv5DZeiQTEE_B|8aa3JOOqT|WEsr|`B=6HEYj&rVJ kWP0UNe`amfIbX(CQ{>B3tXP)obglZzsi, 2015 -# Joakim Soderlund, 2015 +# Joakim Soderlund, 2015-2016 msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Swedish (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/sv/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -19,74 +19,74 @@ msgstr "" "Language: sv\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "Ogiltig \"basic\"-header. Inga användaruppgifter tillhandahölls." -#: authentication.py:74 +#: authentication.py:76 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:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "Ogiltig \"basic\"-header. Användaruppgifterna är inte korrekt base64-kodade." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Ogiltigt användarnamn/lösenord." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "Användaren borttagen eller inaktiv." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "Ogiltig \"token\"-header. Inga användaruppgifter tillhandahölls." -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "Ogiltig \"token\"-header. Strängen ska inte innehålla mellanslag." -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "Ogiltig \"token\"-header. Strängen ska inte innehålla ogiltiga tecken." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Ogiltig \"token\"." #: authtoken/apps.py:7 msgid "Auth Token" -msgstr "" +msgstr "Autentiseringstoken" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" -msgstr "" +msgstr "Nyckel" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" -msgstr "" +msgstr "Användare" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" -msgstr "" +msgstr "Skapad" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" -msgstr "" +msgstr "Token" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" -msgstr "" +msgstr "Tokens" #: authtoken/serializers.py:8 msgid "Username" -msgstr "" +msgstr "Användarnamn" #: authtoken/serializers.py:9 msgid "Password" -msgstr "" +msgstr "Lösenord" #: authtoken/serializers.py:20 msgid "User account is disabled." @@ -125,7 +125,6 @@ msgid "Not found." msgstr "Hittades inte." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "Metoden \"{method}\" tillåts inte." @@ -134,7 +133,6 @@ msgid "Could not satisfy the request Accept header." msgstr "Kunde inte tillfredsställa förfrågans \"Accept\"-header." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "Medietypen \"{media_type}\" stöds inte." @@ -142,214 +140,201 @@ msgstr "Medietypen \"{media_type}\" stöds inte." msgid "Request was throttled." msgstr "Förfrågan stoppades eftersom du har skickat för många." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "Det här fältet är obligatoriskt." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Det här fältet får inte vara null." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" är inte ett giltigt booleskt värde." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "Det här fältet får inte vara blankt." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 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:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "Se till att detta fält har minst {min_length} tecken." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Ange en giltig mejladress." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "Det här värdet matchar inte mallen." -#: fields.py:730 +#: fields.py:735 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:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Ange en giltig URL." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "\"{value} är inte ett giltigt UUID." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "Ange en giltig IPv4- eller IPv6-adress." -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "Ett giltigt heltal krävs." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 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:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 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:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "Textvärdet är för långt." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "Ett giltigt nummer krävs." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 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:888 -#, python-brace-format +#: fields.py:894 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:889 -#, python-brace-format +#: fields.py:895 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:1004 -#, python-brace-format +#: fields.py:1025 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:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "Förväntade en datumtid men fick ett datum." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 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:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "Förväntade ett datum men fick en datumtid." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 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:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "Perioden har fel format. Använd ett av dessa format istället: {format}." -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" är inte ett giltigt val." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "Fler än {count} objekt..." -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 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:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "Det här valet får inte vara tomt." -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "\"{input}\" är inte ett giltigt val för en sökväg." -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "Ingen fil skickades." -#: fields.py:1348 +#: fields.py:1359 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:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "Inget filnamn kunde bestämmas." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "Den skickade filen var tom." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 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:1399 +#: fields.py:1410 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:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "Den här listan får inte vara tom." -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "Förväntade en \"dictionary\" med element men fick typen \"{input_type}\"." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "Värdet måste vara giltig JSON." -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "Skicka" -#: pagination.py:189 -msgid "Invalid page." +#: filters.py:336 +msgid "ascending" msgstr "" -#: pagination.py:407 +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 +msgid "Invalid page." +msgstr "Ogiltig sida." + +#: pagination.py:427 msgid "Invalid cursor" msgstr "Ogiltig cursor." #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Ogiltigt pk \"{pk_value}\" - Objektet finns inte." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "Felaktig typ. Förväntade pk-värde, fick {data_type}." @@ -366,25 +351,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Ogiltig hyperlänk - Objektet finns inte." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Felaktig typ. Förväntade URL-sträng, fick {data_type}." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "Objekt med {slug_name}={value} finns inte." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Ogiltigt värde." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Ogiltig data. Förväntade en dictionary, men fick {datatype}." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "Filter" @@ -414,27 +396,23 @@ msgstr "Inget" msgid "No items to select." msgstr "Inga valbara objekt." -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Det här fältet måste vara unikt." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "Fälten {field_names} måste skapa ett unikt set." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 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:241 -#, python-brace-format +#: validators.py:260 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:254 -#, python-brace-format +#: validators.py:273 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}\"." @@ -442,15 +420,19 @@ msgstr "Det här fältet måste vara unikt för året \"{date_field}\"." msgid "Invalid version in \"Accept\" header." msgstr "Ogiltig version i \"Accept\"-headern." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "Ogiltig version i URL-resursen." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Ogiltig version i värdnamnet." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Ogiltig version i förfrågningsparametern." diff --git a/rest_framework/locale/tr/LC_MESSAGES/django.mo b/rest_framework/locale/tr/LC_MESSAGES/django.mo index 14f5dc981f0819346e1355b1e01fbf482e93b10f..818aad27929ca67ead8eca3a42804446d8192935 100644 GIT binary patch delta 894 zcmXZaO-NKx6u|Lgq1l+GC{8u0H*K`ZhJ57&^f>*n<5yjorA4L+A^L zCtUcsarhJP@DBSz#Jen3)Ec1q+T+Sq_Dp2vASgP*Y&|6&-6 z=uR7J$4gL*RCn1{PqihHPUrI3e`i#F=XMNu1x;{_bXZhV4z zHCw0~{Egg# z2*G)>*u(?uM{SeNzyOx<4j#t3@33v-orik37haQMzN9V ztgs9o#*#;18$L%{_zhXB6%1gQ{_n#W+WZJQ=%H;ei)-*bTFV!37k)u2u#Q1HMv&Xo z3A8+;xDLJR1b%`Uv;=q2PW}X~GfU{hW!#9r(cVe}4`nqb&^nh!Dc%eY!y?)No?!#NLVFW$(enL3 zdovN*W$Pv|j#seVp6_DD4pj(-Wo-Vt^O2M5fl{3z7wf#aQ_FFDt%+)?#06VY;Y#<4-o8e AqyPW_ diff --git a/rest_framework/locale/tr/LC_MESSAGES/django.po b/rest_framework/locale/tr/LC_MESSAGES/django.po index 50722b3d8..17e6e4a73 100644 --- a/rest_framework/locale/tr/LC_MESSAGES/django.po +++ b/rest_framework/locale/tr/LC_MESSAGES/django.po @@ -6,7 +6,7 @@ # Dogukan Tufekci , 2015 # Emrah BİLBAY , 2015 # Ertaç Paprat , 2015 -# José Alaguna , 2016 +# Yusuf (Josè) Luis , 2016 # Mesut Can Gürle , 2015 # Murat Çorlu , 2015 # Recep KIRMIZI , 2015 @@ -15,9 +15,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-09 23:45+0000\n" -"Last-Translator: José Alaguna \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Turkish (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/tr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -25,40 +25,40 @@ msgstr "" "Language: tr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "Geçersiz yetkilendirme başlığı. Gerekli uygunluk kriterleri sağlanmamış." -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "Geçersiz yetkilendirme başlığı. Uygunluk kriterine ait veri boşluk karakteri içermemeli." -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "Geçersiz yetkilendirme başlığı. Uygunluk kriterleri base64 formatına uygun olarak kodlanmamış." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Geçersiz kullanıcı adı/parola" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "Kullanıcı aktif değil ya da silinmiş." -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "Geçersiz token başlığı. Kimlik bilgileri eksik." -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "Geçersiz token başlığı. Token'da boşluk olmamalı." -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "Geçersiz token başlığı. Token geçersiz karakter içermemeli." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Geçersiz token." @@ -66,23 +66,23 @@ msgstr "Geçersiz token." msgid "Auth Token" msgstr "Kimlik doğrulama belirteci" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "Anahtar" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "Kullanan" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "Oluşturulan" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "İşaret" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "İşaretler" @@ -131,7 +131,6 @@ msgid "Not found." msgstr "Bulunamadı." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "\"{method}\" metoduna izin verilmiyor." @@ -140,7 +139,6 @@ msgid "Could not satisfy the request Accept header." msgstr "İsteğe ait Accept başlık bilgisi yanıt verilecek başlık bilgileri arasında değil." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "İstekte desteklenmeyen medya tipi: \"{media_type}\"." @@ -148,214 +146,201 @@ msgstr "İstekte desteklenmeyen medya tipi: \"{media_type}\"." msgid "Request was throttled." msgstr "Üst üste çok fazla istek yapıldı." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "Bu alan zorunlu." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Bu alan boş bırakılmamalı." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" geçerli bir boolean değil." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "Bu alan boş bırakılmamalı." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 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:671 -#, python-brace-format +#: fields.py:676 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:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Geçerli bir e-posta adresi girin." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "Bu değer gereken düzenli ifade deseni ile uyuşmuyor." -#: fields.py:730 +#: fields.py:735 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:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Geçerli bir URL girin." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "\"{value}\" geçerli bir UUID değil." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "Geçerli bir IPv4 ya da IPv6 adresi girin." -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "Geçerli bir tam sayı girin." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 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:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 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:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "String değeri çok uzun." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "Geçerli bir numara gerekiyor." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 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:888 -#, python-brace-format +#: fields.py:894 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:889 -#, python-brace-format +#: fields.py:895 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:1004 -#, python-brace-format +#: fields.py:1025 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:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "Datetime değeri bekleniyor, ama date değeri geldi." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 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:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "Date tipi beklenmekteydi, fakat datetime tipi geldi." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 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:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "Duration biçimi yanlış. {format} biçimlerinden birini kullanın." -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" geçerli bir seçim değil." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "{count} elemandan daha fazla..." -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "Elemanların listesi beklenirken \"{input_type}\" alındı." -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "Bu seçim boş bırakılmamalı." -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "\"{input}\" geçerli bir yol seçimi değil." -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "Hiçbir dosya verilmedi." -#: fields.py:1348 +#: fields.py:1359 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:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "Hiçbir dosya adı belirlenemedi." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "Gönderilen dosya boş." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 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:1399 +#: fields.py:1410 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:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "Bu liste boş olmamalı." -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "Sözlük tipi bir değişken beklenirken \"{input_type}\" tipi bir değişken alındı." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "Değer geçerli bir JSON olmalı." -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "Gönder" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "Geçersiz sayfa." -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "Sayfalandırma imleci geçersiz" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Geçersiz pk \"{pk_value}\" - obje bulunamadı." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "Hatalı tip. Pk değeri beklenirken, alınan {data_type}." @@ -372,25 +357,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Geçersiz bağlantı - Obje bulunamadı." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Hatalı tip. URL metni bekleniyor, {data_type} alındı." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "{slug_name}={value} değerini taşıyan obje bulunamadı." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Geçersiz değer." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Geçersiz veri. Sözlük bekleniyordu fakat {datatype} geldi. " -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "Filtreler" @@ -420,27 +402,23 @@ msgstr "Hiçbiri" msgid "No items to select." msgstr "Seçenek yok." -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Bu alan eşsiz olmalı." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 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:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "Bu alan \"{date_field}\" tarihine göre eşsiz olmalı." -#: validators.py:241 -#, python-brace-format +#: validators.py:260 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:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "Bu alan \"{date_field}\" yılına göre eşsiz olmalı." @@ -448,15 +426,19 @@ msgstr "Bu alan \"{date_field}\" yılına göre eşsiz olmalı." msgid "Invalid version in \"Accept\" header." msgstr "\"Accept\" başlığındaki sürüm geçersiz." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "URL dizininde geçersiz versiyon." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Host adında geçersiz versiyon." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Sorgu parametresinde geçersiz versiyon." diff --git a/rest_framework/locale/tr_TR/LC_MESSAGES/django.mo b/rest_framework/locale/tr_TR/LC_MESSAGES/django.mo index 5fa743b50e80374a743a42495f008818753c0db2..a3a8ca0d57ea9e7fd39683f59274b8adccf003f1 100644 GIT binary patch delta 894 zcmXZaPe{{Y9LMob#Fot>C2HlI{;Xx1to-SgHH=&ewek?AMINg4chcry+rhg03hEkU z5ZWQnQAA;#BqHdhNCc}(T@pO*(y@r%U-lb}*Y|n$?E8G5@AJ%!JsW$I*yw%PApP7a zbxL}@OWKE3Jb-_2FLv&h!gv~^cop059!7A%cL^sMf5$D@*CM6yIF90d9Klt*h}$Fb zG)zz+i%q=6li1iQP2f2!;R`&2t?X`za(D{gqZRrOt-x-o+Jk9K;C0`5w0x_06l3ku zHoVmCNt+3#nV@LR;1QffTeyrjG2lp-F^^|)1)DM2Ar0ao?7=)*C+E-#`G`&U3*-13 zt*{P$Jb~vu0^4vNZQ)mp;TqZo4fOvYwjhU8#`hN5Mh|flzCvsHJGB3QLTcAKc4Lr9 zZd1KT4a(qF^llS`2=1UIcz|~D$7r2d#voSF3i^SzaSa2wg9l@si=Y*f#1tOG5u8E0 znh$6PtRlCd^}3z{WJ@v;!+xCf$B)n!enzW&4ITW80~p;e4dDbf;8SeG1++Ku5-s01 zv^Nv#s;|3%F5@fMWzTn(z$#lpYt0JUUDg&e!CF&jAnx`#?%|Z%|0Er5OV@|Vl$+@3 y45)vgV~Q= zW(UmdRg>9%tl}a3gZr?p*(`zsxCgIdE8fK@&evVUi;TbFHcYmd^2+4vsl6xcp6)%u0&%vfE84u|4PZkqMf%8O-1;YT*)&VZbrFf(1N-%NWM!Ub7xdU?&z(C;0@`$a`$WpP0bk zsK#Rac>+g#0u`J?E&PIQxQZ$et06{x{sUjCF;oEqQ3ut^loc-5H~W( zZCV%7gXM8M`V#~pg4?JB_fR{3h&q`i3}O}4(05efb=-tIc`!P;W>h0CX7L#I;|%I* z-k~;FMQ*{?YWfx++Yu(>=;HbM_yKC+M^xvl=-^*GiqUqnK0JpF_!zh1JnBs>pz?i1 zy_trN+PYCpF}{o)dcLy+>TD5pG|Q;FT(6u8{%Q=liDb%2WwLJOQ7+Qj%dp!y>}J!M z&LqFkkXN2gjFh~|a?zWfDrKF)sq&`x{<-YNo-bAtTJaQ-eAF;G? A9smFU diff --git a/rest_framework/locale/tr_TR/LC_MESSAGES/django.po b/rest_framework/locale/tr_TR/LC_MESSAGES/django.po index efbd689e6..171826a63 100644 --- a/rest_framework/locale/tr_TR/LC_MESSAGES/django.po +++ b/rest_framework/locale/tr_TR/LC_MESSAGES/django.po @@ -3,14 +3,14 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: -# José Alaguna , 2015-2016 +# Yusuf (Josè) Luis , 2015-2016 msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-09 23:48+0000\n" -"Last-Translator: José Alaguna \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Turkish (Turkey) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/tr_TR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -18,40 +18,40 @@ msgstr "" "Language: tr_TR\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "Geçersiz yetkilendirme başlığı. Gerekli uygunluk kriterleri sağlanmamış." -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "Geçersiz yetkilendirme başlığı. Uygunluk kriterine ait veri boşluk karakteri içermemeli." -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "Geçersiz yetkilendirme başlığı. Uygunluk kriterleri base64 formatına uygun olarak kodlanmamış." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "Geçersiz kullanıcı adı / şifre." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "Kullanıcı aktif değil ya da silinmiş" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "Geçersiz token başlığı. Kimlik bilgileri eksik." -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "Geçersiz token başlığı. Token'da boşluk olmamalı." -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "Geçersiz token başlığı. Token geçersiz karakter içermemeli." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "Geçersiz simge." @@ -59,23 +59,23 @@ msgstr "Geçersiz simge." msgid "Auth Token" msgstr "Kimlik doğrulama belirteci" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "Anahtar" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "Kullanan" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "Oluşturulan" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "İşaret" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "İşaretler" @@ -124,7 +124,6 @@ msgid "Not found." msgstr "Bulunamadı." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "\"{method}\" metoduna izin verilmiyor." @@ -133,7 +132,6 @@ msgid "Could not satisfy the request Accept header." msgstr "İsteğe ait Accept başlık bilgisi yanıt verilecek başlık bilgileri arasında değil." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "İstekte desteklenmeyen medya tipi: \"{media_type}\"." @@ -141,214 +139,201 @@ msgstr "İstekte desteklenmeyen medya tipi: \"{media_type}\"." msgid "Request was throttled." msgstr "Üst üste çok fazla istek yapıldı." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "Bu alan zorunlu." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "Bu alan boş bırakılmamalı." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "\"{input}\" geçerli bir boolean değil." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "Bu alan boş bırakılmamalı." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 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:671 -#, python-brace-format +#: fields.py:676 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:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "Geçerli bir e-posta adresi girin." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "Bu değer gereken düzenli ifade deseni ile uyuşmuyor." -#: fields.py:730 +#: fields.py:735 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:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "Geçerli bir URL girin." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "\"{value}\" geçerli bir UUID değil." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "Geçerli bir IPv4 ya da IPv6 adresi girin." -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "Geçerli bir tam sayı girin." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 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:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 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:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "String değeri çok uzun." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "Geçerli bir numara gerekiyor." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 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:888 -#, python-brace-format +#: fields.py:894 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:889 -#, python-brace-format +#: fields.py:895 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:1004 -#, python-brace-format +#: fields.py:1025 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:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "Datetime değeri bekleniyor, ama date değeri geldi." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 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:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "Date tipi beklenmekteydi, fakat datetime tipi geldi." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 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:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "Duration biçimi yanlış. {format} biçimlerinden birini kullanın." -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "\"{input}\" geçerli bir seçim değil." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "{count} elemandan daha fazla..." -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "Elemanların listesi beklenirken \"{input_type}\" alındı." -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "Bu seçim boş bırakılmamalı." -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "\"{input}\" geçerli bir yol seçimi değil." -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "Hiçbir dosya verilmedi." -#: fields.py:1348 +#: fields.py:1359 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:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "Hiçbir dosya adı belirlenemedi." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "Gönderilen dosya boş." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 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:1399 +#: fields.py:1410 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:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "Bu liste boş olmamalı." -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "Sözlük tipi bir değişken beklenirken \"{input_type}\" tipi bir değişken alındı." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "Değer geçerli bir JSON olmalı." -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "Gönder" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "Geçersiz sayfa." -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "Geçersiz imleç." #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "Geçersiz pk \"{pk_value}\" - obje bulunamadı." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "Hatalı tip. Pk değeri beklenirken, alınan {data_type}." @@ -365,25 +350,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "Geçersiz hyper link - Nesne yok.." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "Hatalı tip. URL metni bekleniyor, {data_type} alındı." -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "{slug_name}={value} değerini taşıyan obje bulunamadı." -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "Geçersiz değer." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "Geçersiz veri. Bir sözlük bekleniyor, ama var {datatype}." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "Filtreler" @@ -413,27 +395,23 @@ msgstr "Hiç kimse" msgid "No items to select." msgstr "Seçenek yok." -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "Bu alan benzersiz olmalıdır." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "{field_names} alanları benzersiz bir set yapmak gerekir." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "Bu alan \"{date_field}\" tarihine göre eşsiz olmalı." -#: validators.py:241 -#, python-brace-format +#: validators.py:260 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:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "Bu alan \"{date_field}\" yılına göre eşsiz olmalı." @@ -441,15 +419,19 @@ msgstr "Bu alan \"{date_field}\" yılına göre eşsiz olmalı." msgid "Invalid version in \"Accept\" header." msgstr "\"Kabul et\" başlığında geçersiz sürümü." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "URL yolu geçersiz sürümü." -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "Hostname geçersiz sürümü." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "Sorgu parametresi geçersiz sürümü." diff --git a/rest_framework/locale/uk/LC_MESSAGES/django.mo b/rest_framework/locale/uk/LC_MESSAGES/django.mo index 79396f543536eb6d2a2bb3842c62d7733c575cad..bfcb776e763fa2dc91969ef5117861e5c817186e 100644 GIT binary patch literal 13352 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, 2016 +# Illarion , 2016 +# Kirill Tarasenko, 2016 msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Ukrainian (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/uk/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,441 +20,423 @@ msgstr "" "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:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." -msgstr "" +msgstr "Недійсний основний заголовок. Облікові дані відсутні." -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." -msgstr "" +msgstr "Недійсний основний заголовок. Облікові дані мають бути без пробілів." -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." -msgstr "" +msgstr "Недійсний основний заголовок. Облікові дані невірно закодовані у Base64." -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." -msgstr "" +msgstr "Недійсне iм'я користувача/пароль." -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." -msgstr "" - -#: authentication.py:173 -msgid "Invalid token header. No credentials provided." -msgstr "" +msgstr "Користувач неактивний або видалений." #: authentication.py:176 -msgid "Invalid token header. Token string should not contain spaces." -msgstr "" +msgid "Invalid token header. No credentials provided." +msgstr "Недійсний заголовок токена. Облікові дані відсутні." -#: authentication.py:182 +#: authentication.py:179 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "Недійсний заголовок токена. Значення токена не повинне містити пробіли." + +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." -msgstr "" +msgstr "Недійсний заголовок токена. Значення токена не повинне містити некоректні символи." -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." -msgstr "" +msgstr "Недійсний токен." #: authtoken/apps.py:7 msgid "Auth Token" -msgstr "" +msgstr "Авторизаційний токен" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" -msgstr "" +msgstr "Ключ" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" -msgstr "" +msgstr "Користувач" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" -msgstr "" +msgstr "Створено" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" -msgstr "" +msgstr "Токен" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" -msgstr "" +msgstr "Токени" #: authtoken/serializers.py:8 msgid "Username" -msgstr "" +msgstr "Ім'я користувача" #: authtoken/serializers.py:9 msgid "Password" -msgstr "" +msgstr "Пароль" #: authtoken/serializers.py:20 msgid "User account is disabled." -msgstr "" +msgstr "Обліковий запис деактивований." #: authtoken/serializers.py:23 msgid "Unable to log in with provided credentials." -msgstr "" +msgstr "Неможливо зайти з введеними даними." #: authtoken/serializers.py:26 msgid "Must include \"username\" and \"password\"." -msgstr "" +msgstr "Має включати iм'я користувача та пароль" #: exceptions.py:49 msgid "A server error occurred." -msgstr "" +msgstr "Помилка сервера." #: exceptions.py:84 msgid "Malformed request." -msgstr "" +msgstr "Некоректний запит." #: exceptions.py:89 msgid "Incorrect authentication credentials." -msgstr "" +msgstr "Некоректні реквізити перевірки достовірності." #: exceptions.py:94 msgid "Authentication credentials were not provided." -msgstr "" +msgstr "Реквізити перевірки достовірності не надані." #: exceptions.py:99 msgid "You do not have permission to perform this action." -msgstr "" +msgstr "У вас нема дозволу робити цю дію." #: exceptions.py:104 views.py:81 msgid "Not found." -msgstr "" +msgstr "Не знайдено." #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." -msgstr "" +msgstr "Метод \"{method}\" не дозволений." #: exceptions.py:120 msgid "Could not satisfy the request Accept header." -msgstr "" +msgstr "Неможливо виконати запит прийняття заголовку." #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." -msgstr "" +msgstr "Непідтримуваний тип даних \"{media_type}\" в запиті." #: exceptions.py:145 msgid "Request was throttled." -msgstr "" +msgstr "Запит було проігноровано." -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." -msgstr "" +msgstr "Це поле обов'язкове." -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." -msgstr "" +msgstr "Це поле не може бути null." -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." -msgstr "" +msgstr "\"{input}\" не є коректним бульовим значенням." -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." -msgstr "" +msgstr "Це поле не може бути порожнім." -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." -msgstr "" +msgstr "Переконайтесь, що кількість символів в цьому полі не перевищує {max_length}." -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." -msgstr "" +msgstr "Переконайтесь, що в цьому полі мінімум {min_length} символів." -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." -msgstr "" +msgstr "Введіть коректну адресу електронної пошти." -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." -msgstr "" +msgstr "Значення не відповідає необхідному патерну." -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." -msgstr "" +msgstr "Введіть коректний \"slug\", що складається із букв, цифр, нижніх підкреслень або дефісів. " -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." -msgstr "" +msgstr "Введіть коректний URL." -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." -msgstr "" +msgstr "\"{value}\" не є коректним UUID." -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." -msgstr "" +msgstr "Введіть дійсну IPv4 або IPv6 адресу." -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." -msgstr "" +msgstr "Необхідне цілочисельне значення." -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." -msgstr "" +msgstr "Переконайтесь, що значення менше або дорівнює {max_value}." -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." -msgstr "" +msgstr "Переконайтесь, що значення більше або дорівнює {min_value}." -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." -msgstr "" +msgstr "Строкове значення занадто велике." -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." -msgstr "" +msgstr "Необхідне чисельне значення." -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." -msgstr "" +msgstr "Переконайтесь, що в числі не більше {max_digits} знаків." -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." -msgstr "" +msgstr "Переконайтесь, що в числі не більше {max_decimal_places} знаків у дробовій частині." -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." -msgstr "" +msgstr "Переконайтесь, що в числі не більше {max_whole_digits} знаків у цілій частині." -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." -msgstr "" +msgstr "Невірний формат дата з часом. Використайте один з цих форматів: {format}." -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." -msgstr "" +msgstr "Очікувалась дата з часом, але було отримано дату." -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." -msgstr "" +msgstr "Невірний формат дати. Використайте один з цих форматів: {format}." -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." -msgstr "" +msgstr "Очікувалась дата, але було отримано дату з часом." -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." -msgstr "" +msgstr "Неправильний формат часу. Використайте один з цих форматів: {format}." -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." -msgstr "" +msgstr "Невірний формат тривалості. Використайте один з цих форматів: {format}." -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." -msgstr "" +msgstr "\"{input}\" не є коректним вибором." -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." -msgstr "" +msgstr "Елементів більше, ніж {count}..." -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." -msgstr "" +msgstr "Очікувався список елементів, але було отримано \"{input_type}\"." -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." -msgstr "" +msgstr "Вибір не може бути порожнім." -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." -msgstr "" +msgstr "\"{input}\" вибраний шлях не є коректним." -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." -msgstr "" +msgstr "Файл не було відправленно." -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." -msgstr "" +msgstr "Відправленні дані не є файл. Перевірте тип кодування у формі." -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." -msgstr "" +msgstr "Неможливо визначити ім'я файлу." -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." -msgstr "" +msgstr "Відправленний файл порожній." -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." -msgstr "" +msgstr "Переконайтесь, що ім'я файлу становить менше {max_length} символів (зараз {length})." -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." -msgstr "" +msgstr "Завантажте коректне зображення. Завантажений файл або не є зображенням, або пошкоджений." -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." -msgstr "" +msgstr "Цей список не може бути порожнім." -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." -msgstr "" +msgstr "Очікувався словник зі елементами, але було отримано \"{input_type}\"." -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." -msgstr "" +msgstr "Значення повинно бути коректним JSON." -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" +msgstr "Відправити" + +#: filters.py:336 +msgid "ascending" msgstr "" -#: pagination.py:189 +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." -msgstr "" +msgstr "Недійсна сторінка." -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" -msgstr "" +msgstr "Недійсний курсор." #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." -msgstr "" +msgstr "Недопустимий первинний ключ \"{pk_value}\" - об'єкт не існує." #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." -msgstr "" +msgstr "Некоректний тип. Очікувалось значення первинного ключа, отримано {data_type}." #: relations.py:240 msgid "Invalid hyperlink - No URL match." -msgstr "" +msgstr "Недійсне посилання - немає збігу за URL." #: relations.py:241 msgid "Invalid hyperlink - Incorrect URL match." -msgstr "" +msgstr "Недійсне посилання - некоректний збіг за URL." #: relations.py:242 msgid "Invalid hyperlink - Object does not exist." -msgstr "" +msgstr "Недійсне посилання - об'єкт не існує." #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." -msgstr "" +msgstr "Некоректний тип. Очікувався URL, отримано {data_type}." + +#: relations.py:401 +msgid "Object with {slug_name}={value} does not exist." +msgstr "Об'єкт із {slug_name}={value} не існує." #: relations.py:402 -#, python-brace-format -msgid "Object with {slug_name}={value} does not exist." -msgstr "" - -#: relations.py:403 msgid "Invalid value." -msgstr "" +msgstr "Недійсне значення." #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." -msgstr "" +msgstr "Недопустимі дані. Очікувався словник, але було отримано {datatype}." -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" -msgstr "" +msgstr "Фільтри" #: templates/rest_framework/filters/django_filter.html:2 #: templates/rest_framework/filters/django_filter_crispyforms.html:4 msgid "Field filters" -msgstr "" +msgstr "Фільтри поля" #: templates/rest_framework/filters/ordering.html:3 msgid "Ordering" -msgstr "" +msgstr "Впорядкування" #: templates/rest_framework/filters/search.html:2 msgid "Search" -msgstr "" +msgstr "Пошук" #: templates/rest_framework/horizontal/radio.html:2 #: templates/rest_framework/inline/radio.html:2 #: templates/rest_framework/vertical/radio.html:2 msgid "None" -msgstr "" +msgstr "Нічого" #: templates/rest_framework/horizontal/select_multiple.html:2 #: templates/rest_framework/inline/select_multiple.html:2 #: templates/rest_framework/vertical/select_multiple.html:2 msgid "No items to select." -msgstr "" +msgstr "Немає елементів для вибору." -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." -msgstr "" +msgstr "Це поле повинне бути унікальним." -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." -msgstr "" +msgstr "Поля {field_names} повинні створювати унікальний масив значень." -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." -msgstr "" +msgstr "Це поле повинне бути унікальним для дати \"{date_field}\"." -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." -msgstr "" +msgstr "Це поле повинне бути унікальним для місяця \"{date_field}\"." -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." -msgstr "" +msgstr "Це поле повинне бути унікальним для року \"{date_field}\"." #: versioning.py:42 msgid "Invalid version in \"Accept\" header." -msgstr "" +msgstr "Недопустима версія в загаловку \"Accept\"." -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." +msgstr "Недопустима версія в шляху URL." + +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." msgstr "" -#: versioning.py:144 +#: versioning.py:147 msgid "Invalid version in hostname." -msgstr "" +msgstr "Недопустима версія в імені хоста." -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." -msgstr "" +msgstr "Недопустима версія в параметрі запиту." #: views.py:88 msgid "Permission denied." -msgstr "" +msgstr "Доступ заборонено." diff --git a/rest_framework/locale/vi/LC_MESSAGES/django.mo b/rest_framework/locale/vi/LC_MESSAGES/django.mo index a055d763a364964c42cb63ea396a2ada7557adaf..578308acfd9abd19134b6dd40f85fb5ecbce465e 100644 GIT binary patch delta 90 zcmeyz{EvCUWG-`ELn8%4Gb=;miF4%~Av{wnLlbQSAmH*zEH2RvDN4*M&PgoEFS1ey h$;i)5ELL#ND9S7@$xKzSDap@u0ODk*z{WpJi~#Ap9o+x` delta 90 zcmeyz{EvCUWG-V}149Kv3oB!biF4%~Av|*+PulKW2=@Mjjsk diff --git a/rest_framework/locale/vi/LC_MESSAGES/django.po b/rest_framework/locale/vi/LC_MESSAGES/django.po index 0f8415b51..ea43efb95 100644 --- a/rest_framework/locale/vi/LC_MESSAGES/django.po +++ b/rest_framework/locale/vi/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Vietnamese (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/vi/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: vi\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/zh_CN/LC_MESSAGES/django.mo b/rest_framework/locale/zh_CN/LC_MESSAGES/django.mo index ecd7a91eb16f364fa0360fc7fbd39b25a49c22be..5ba81a865ad7aef3a93f1a08a8d83700339ef844 100644 GIT binary patch delta 93 zcmbR4H{EZ;RWUAeT|*-ULo+KwoEH24RRj?_^&vgLeWT?PqJMo+R06wf9w*UYD delta 93 zcmbR4H{EZ;RWUAOT?0b}LklZoi^=!J\n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Chinese (China) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/zh_CN/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -20,40 +20,40 @@ msgstr "" "Language: zh_CN\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "无效的Basic认证头,没有提供认证信息。" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "认证字符串不应该包含空格(基本认证HTTP头无效)。" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "认证字符串base64编码错误(基本认证HTTP头无效)。" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "用户名或者密码错误。" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "用户未激活或者已删除。" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "没有提供认证信息(认证令牌HTTP头无效)。" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "认证令牌字符串不应该包含空格(无效的认证令牌HTTP头)。" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "无效的Token。Token字符串不能包含非法的字符。" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "认证令牌无效。" @@ -61,23 +61,23 @@ msgstr "认证令牌无效。" msgid "Auth Token" msgstr "认证令牌" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "用户" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "令牌" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "令牌" @@ -126,7 +126,6 @@ msgid "Not found." msgstr "未找到。" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "方法 “{method}” 不被允许。" @@ -135,7 +134,6 @@ msgid "Could not satisfy the request Accept header." msgstr "无法满足Accept HTTP头的请求。" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "不支持请求中的媒体类型 “{media_type}”。" @@ -143,214 +141,201 @@ msgstr "不支持请求中的媒体类型 “{media_type}”。" msgid "Request was throttled." msgstr "请求超过了限速。" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "该字段是必填项。" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "该字段不能为 null。" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "“{input}” 不是合法的布尔值。" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "该字段不能为空。" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "请确保这个字段不能超过 {max_length} 个字符。" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "请确保这个字段至少包含 {min_length} 个字符。" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "请输入合法的邮件地址。" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "输入值不匹配要求的模式。" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "请输入合法的“短语“,只能包含字母,数字,下划线或者中划线。" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "请输入合法的URL。" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "“{value}”不是合法的UUID。" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "请输入一个有效的IPv4或IPv6地址。" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "请填写合法的整数值。" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "请确保该值小于或者等于 {max_value}。" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "请确保该值大于或者等于 {min_value}。" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "字符串值太长。" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "请填写合法的数字。" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "请确保总计不超过 {max_digits} 个数字。" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "请确保总计不超过 {max_decimal_places} 个小数位。" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "请确保小数点前不超过 {max_whole_digits} 个数字。" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "日期时间格式错误。请从这些格式中选择:{format}。" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "期望为日期时间,得到的是日期。" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "日期格式错误。请从这些格式中选择:{format}。" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "期望为日期,得到的是日期时间。" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "时间格式错误。请从这些格式中选择:{format}。" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "持续时间的格式错误。使用这些格式中的一个:{format}。" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "“{input}” 不是合法选项。" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "多于{count}条记录。" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "期望为一个包含物件的列表,得到的类型是“{input_type}”。" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "这项选择不能为空。" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "“{input}” 不是有效路径选项。" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "没有提交任何文件。" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "提交的数据不是一个文件。请检查表单的编码类型。" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "无法检测到文件名。" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "提交的是空文件。" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "确保该文件名最多包含 {max_length} 个字符 ( 当前长度为{length} ) 。" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "请上传有效图片。您上传的该文件不是图片或者图片已经损坏。" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "列表字段不能为空值。" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "期望是包含类目的字典,得到类型为 “{input_type}”。" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "值必须是有效的 JSON 数据。" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "保存" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "无效游标" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "无效主键 “{pk_value}” - 对象不存在。" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "类型错误。期望为主键,得到的类型为 {data_type}。" @@ -367,25 +352,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "无效超链接 -对象不存在。" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "类型错误。期望为URL字符串,实际的类型是 {data_type}。" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "属性 {slug_name} 为 {value} 的对象不存在。" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "无效值。" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "无效数据。期待为字典类型,得到的是 {datatype} 。" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "过滤器" @@ -415,27 +397,23 @@ msgstr "无" msgid "No items to select." msgstr "没有可选项。" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "该字段必须唯一。" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "字段 {field_names} 必须能构成唯一集合。" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "该字段必须在日期 “{date_field}” 唯一。" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "该字段必须在月份 “{date_field}” 唯一。" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "该字段必须在年 “{date_field}” 唯一。" @@ -443,15 +421,19 @@ msgstr "该字段必须在年 “{date_field}” 唯一。" msgid "Invalid version in \"Accept\" header." msgstr "“Accept” HTTP头包含无效版本。" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "URL路径包含无效版本。" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "主机名包含无效版本。" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "请求参数里包含无效版本。" diff --git a/rest_framework/locale/zh_Hans/LC_MESSAGES/django.mo b/rest_framework/locale/zh_Hans/LC_MESSAGES/django.mo index cffdae362001044baa09a3a24b87ea50bab071ad..396aded071dcd23c423b058c51168a736bea1410 100644 GIT binary patch delta 93 zcmX@&bHrzZnHZP3uAz~Fp_!GT@ni=vIY$WB)XLCA+W-i-d=iUGbVG^~^NMp4OY)1X k6hboca}$ddoHL3ti%T+76>LiKa~*&<87i=OzSvTJ0Qv+S`v3p{ delta 93 zcmX@&bHrzZnHZO`u7RO~p@o&P#bgIDIY$WB9LUu+00J(b#Nra&kfOxA;+({i{30ub lh{UqY)FK7{qLlo?(n, 2015 +# cokky , 2015 # hunter007 , 2015 # nypisces , 2015 msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Chinese Simplified (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/zh-Hans/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -20,40 +20,40 @@ msgstr "" "Language: zh-Hans\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "无效的Basic认证头,没有提供认证信息。" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "认证字符串不应该包含空格(基本认证HTTP头无效)。" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "认证字符串base64编码错误(基本认证HTTP头无效)。" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "用户名或者密码错误。" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "用户未激活或者已删除。" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "没有提供认证信息(认证令牌HTTP头无效)。" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "认证令牌字符串不应该包含空格(无效的认证令牌HTTP头)。" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "无效的Token。Token字符串不能包含非法的字符。" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "认证令牌无效。" @@ -61,23 +61,23 @@ msgstr "认证令牌无效。" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -126,7 +126,6 @@ msgid "Not found." msgstr "未找到。" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "方法 “{method}” 不被允许。" @@ -135,7 +134,6 @@ msgid "Could not satisfy the request Accept header." msgstr "无法满足Accept HTTP头的请求。" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "不支持请求中的媒体类型 “{media_type}”。" @@ -143,214 +141,201 @@ msgstr "不支持请求中的媒体类型 “{media_type}”。" msgid "Request was throttled." msgstr "请求超过了限速。" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "该字段是必填项。" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "该字段不能为 null。" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "“{input}” 不是合法的布尔值。" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "该字段不能为空。" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "请确保这个字段不能超过 {max_length} 个字符。" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "请确保这个字段至少包含 {min_length} 个字符。" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "请输入合法的邮件地址。" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "输入值不匹配要求的模式。" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "请输入合法的“短语“,只能包含字母,数字,下划线或者中划线。" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "请输入合法的URL。" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "“{value}”不是合法的UUID。" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "请输入一个有效的IPv4或IPv6地址。" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "请填写合法的整数值。" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "请确保该值小于或者等于 {max_value}。" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "请确保该值大于或者等于 {min_value}。" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "字符串值太长。" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "请填写合法的数字。" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "请确保总计不超过 {max_digits} 个数字。" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "请确保总计不超过 {max_decimal_places} 个小数位。" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "请确保小数点前不超过 {max_whole_digits} 个数字。" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "日期时间格式错误。请从这些格式中选择:{format}。" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "期望为日期时间,获得的是日期。" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "日期格式错误。请从这些格式中选择:{format}。" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "期望为日期,获得的是日期时间。" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "时间格式错误。请从这些格式中选择:{format}。" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "持续时间的格式错误。使用这些格式中的一个:{format}。" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "“{input}” 不是合法选项。" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "多于{count}条记录。" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "期望为一个包含物件的列表,得到的类型是“{input_type}”。" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "这项选择不能为空。" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "\"{input}\"不是一个有效路径选项。" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "没有提交任何文件。" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "提交的数据不是一个文件。请检查表单的编码类型。" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "无法检测到文件名。" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "提交的是空文件。" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "确保该文件名最多包含 {max_length} 个字符 ( 当前长度为{length} ) 。" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "请上传有效图片。您上传的该文件不是图片或者图片已经损坏。" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "列表不能为空。" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "期望是包含类目的字典,得到类型为 “{input_type}”。" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "值必须是有效的 JSON 数据。" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "提交" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "无效游标" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "无效主键 “{pk_value}” - 对象不存在。" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "类型错误。期望为主键,获得的类型为 {data_type}。" @@ -367,25 +352,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "无效超链接 -对象不存在。" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "类型错误。期望为URL字符串,实际的类型是 {data_type}。" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "属性 {slug_name} 为 {value} 的对象不存在。" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "无效值。" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "无效数据。期待为字典类型,得到的是 {datatype} 。" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "过滤器" @@ -415,27 +397,23 @@ msgstr "无" msgid "No items to select." msgstr "没有可选项。" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "该字段必须唯一。" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "字段 {field_names} 必须能构成唯一集合。" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "该字段必须在日期 “{date_field}” 唯一。" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "该字段必须在月份 “{date_field}” 唯一。" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "该字段必须在年 “{date_field}” 唯一。" @@ -443,15 +421,19 @@ msgstr "该字段必须在年 “{date_field}” 唯一。" msgid "Invalid version in \"Accept\" header." msgstr "“Accept” HTTP头包含无效版本。" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "URL路径包含无效版本。" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "主机名包含无效版本。" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "请求参数里包含无效版本。" diff --git a/rest_framework/locale/zh_Hant/LC_MESSAGES/django.mo b/rest_framework/locale/zh_Hant/LC_MESSAGES/django.mo index d33604524b1bd71b84486a858d72aeb44b7f192c..08954cc4b5b4a72098439b06e44d3f66a3a0aba6 100644 GIT binary patch delta 90 zcmbQpGLdD%WG-`ELn8%4Gb=;miF4%~Av{wnLlbQSAmH*zEH2RvDN4*M&PgoEFS1ey h$;i)5ELL#ND9S7@$xKzSDap@u0ODk*z{WqKi~yd<9T)%r delta 90 zcmbQpGLdD%WG-V}149Kv3oB!biF4%~Av|*+PulKWU34QXN_V diff --git a/rest_framework/locale/zh_Hant/LC_MESSAGES/django.po b/rest_framework/locale/zh_Hant/LC_MESSAGES/django.po index ea6a45312..1960f1f5d 100644 --- a/rest_framework/locale/zh_Hant/LC_MESSAGES/django.po +++ b/rest_framework/locale/zh_Hant/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Chinese Traditional (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/zh-Hant/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: zh-Hant\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/locale/zh_TW/LC_MESSAGES/django.mo b/rest_framework/locale/zh_TW/LC_MESSAGES/django.mo index 6371fcea7fd84e256c8240ad704f5cdbe3ced0c9..b3158f9eb92fc5e7e91b9b98d96ef3e651fe3bf3 100644 GIT binary patch delta 90 zcmeBT>0+5Mnaf<)&`811%*xPs;#@gL2+!2Y&_vq+2)KL_i%WDviW2jRa}rDPi>wqv hGV*g1ixr$RiZY8!GE)_7O7e3ZfH)Z{u<;KcBLJD)9RmOW delta 90 zcmeBT>0+5Mnafz$z)-=^!phiU;#@gL2+thI(>4GCE}z8W65WuZ#Ju91#FG3XD}{)} ivdq*X1^=Ry{KC>o1)GX|2+JWSGcUg^GjZb|K1KkbW*tQU diff --git a/rest_framework/locale/zh_TW/LC_MESSAGES/django.po b/rest_framework/locale/zh_TW/LC_MESSAGES/django.po index 8858ce17c..9bfb23c6b 100644 --- a/rest_framework/locale/zh_TW/LC_MESSAGES/django.po +++ b/rest_framework/locale/zh_TW/LC_MESSAGES/django.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-03-01 18:38+0100\n" -"PO-Revision-Date: 2016-03-01 17:38+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2016-07-12 16:13+0100\n" +"PO-Revision-Date: 2016-07-12 15:14+0000\n" +"Last-Translator: Thomas Christie \n" "Language-Team: Chinese (Taiwan) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/zh_TW/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -17,40 +17,40 @@ msgstr "" "Language: zh_TW\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: authentication.py:71 +#: authentication.py:73 msgid "Invalid basic header. No credentials provided." msgstr "" -#: authentication.py:74 +#: authentication.py:76 msgid "Invalid basic header. Credentials string should not contain spaces." msgstr "" -#: authentication.py:80 +#: authentication.py:82 msgid "Invalid basic header. Credentials not correctly base64 encoded." msgstr "" -#: authentication.py:97 +#: authentication.py:99 msgid "Invalid username/password." msgstr "" -#: authentication.py:100 authentication.py:195 +#: authentication.py:102 authentication.py:198 msgid "User inactive or deleted." msgstr "" -#: authentication.py:173 +#: authentication.py:176 msgid "Invalid token header. No credentials provided." msgstr "" -#: authentication.py:176 +#: authentication.py:179 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: authentication.py:182 +#: authentication.py:185 msgid "" "Invalid token header. Token string should not contain invalid characters." msgstr "" -#: authentication.py:192 +#: authentication.py:195 msgid "Invalid token." msgstr "" @@ -58,23 +58,23 @@ msgstr "" msgid "Auth Token" msgstr "" -#: authtoken/models.py:21 +#: authtoken/models.py:15 msgid "Key" msgstr "" -#: authtoken/models.py:23 +#: authtoken/models.py:18 msgid "User" msgstr "" -#: authtoken/models.py:24 +#: authtoken/models.py:20 msgid "Created" msgstr "" -#: authtoken/models.py:33 +#: authtoken/models.py:29 msgid "Token" msgstr "" -#: authtoken/models.py:34 +#: authtoken/models.py:30 msgid "Tokens" msgstr "" @@ -123,7 +123,6 @@ msgid "Not found." msgstr "" #: exceptions.py:109 -#, python-brace-format msgid "Method \"{method}\" not allowed." msgstr "" @@ -132,7 +131,6 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:132 -#, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." msgstr "" @@ -140,214 +138,201 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:266 relations.py:206 relations.py:239 validators.py:79 -#: validators.py:162 +#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 +#: validators.py:181 msgid "This field is required." msgstr "" -#: fields.py:267 +#: fields.py:270 msgid "This field may not be null." msgstr "" -#: fields.py:603 fields.py:634 -#, python-brace-format +#: fields.py:608 fields.py:639 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:669 +#: fields.py:674 msgid "This field may not be blank." msgstr "" -#: fields.py:670 fields.py:1664 -#, python-brace-format +#: fields.py:675 fields.py:1675 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:671 -#, python-brace-format +#: fields.py:676 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: fields.py:708 +#: fields.py:713 msgid "Enter a valid email address." msgstr "" -#: fields.py:719 +#: fields.py:724 msgid "This value does not match the required pattern." msgstr "" -#: fields.py:730 +#: fields.py:735 msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." msgstr "" -#: fields.py:742 +#: fields.py:747 msgid "Enter a valid URL." msgstr "" -#: fields.py:755 -#, python-brace-format +#: fields.py:760 msgid "\"{value}\" is not a valid UUID." msgstr "" -#: fields.py:791 +#: fields.py:796 msgid "Enter a valid IPv4 or IPv6 address." msgstr "" -#: fields.py:816 +#: fields.py:821 msgid "A valid integer is required." msgstr "" -#: fields.py:817 fields.py:852 fields.py:885 -#, python-brace-format +#: fields.py:822 fields.py:857 fields.py:891 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:818 fields.py:853 fields.py:886 -#, python-brace-format +#: fields.py:823 fields.py:858 fields.py:892 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:819 fields.py:854 fields.py:890 +#: fields.py:824 fields.py:859 fields.py:896 msgid "String value too large." msgstr "" -#: fields.py:851 fields.py:884 +#: fields.py:856 fields.py:890 msgid "A valid number is required." msgstr "" -#: fields.py:887 -#, python-brace-format +#: fields.py:893 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:888 -#, python-brace-format +#: fields.py:894 msgid "" "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:889 -#, python-brace-format +#: fields.py:895 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:1004 -#, python-brace-format +#: fields.py:1025 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1005 +#: fields.py:1026 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:1082 -#, python-brace-format +#: fields.py:1103 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1083 +#: fields.py:1104 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:1151 -#, python-brace-format +#: fields.py:1170 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1215 -#, python-brace-format +#: fields.py:1232 msgid "Duration has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:1240 fields.py:1289 -#, python-brace-format +#: fields.py:1251 fields.py:1300 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1243 relations.py:71 relations.py:442 -#, python-brace-format +#: fields.py:1254 relations.py:71 relations.py:441 msgid "More than {count} items..." msgstr "" -#: fields.py:1290 fields.py:1437 relations.py:438 serializers.py:520 -#, python-brace-format +#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1291 +#: fields.py:1302 msgid "This selection may not be empty." msgstr "" -#: fields.py:1328 -#, python-brace-format +#: fields.py:1339 msgid "\"{input}\" is not a valid path choice." msgstr "" -#: fields.py:1347 +#: fields.py:1358 msgid "No file was submitted." msgstr "" -#: fields.py:1348 +#: fields.py:1359 msgid "" "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1349 +#: fields.py:1360 msgid "No filename could be determined." msgstr "" -#: fields.py:1350 +#: fields.py:1361 msgid "The submitted file is empty." msgstr "" -#: fields.py:1351 -#, python-brace-format +#: fields.py:1362 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1399 +#: fields.py:1410 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: fields.py:1438 relations.py:439 serializers.py:521 +#: fields.py:1449 relations.py:438 serializers.py:525 msgid "This list may not be empty." msgstr "" -#: fields.py:1491 -#, python-brace-format +#: fields.py:1502 msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1538 +#: fields.py:1549 msgid "Value must be valid JSON." msgstr "" -#: filters.py:35 templates/rest_framework/filters/django_filter.html.py:5 +#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 msgid "Submit" msgstr "" -#: pagination.py:189 +#: filters.py:336 +msgid "ascending" +msgstr "" + +#: filters.py:337 +msgid "descending" +msgstr "" + +#: pagination.py:193 msgid "Invalid page." msgstr "" -#: pagination.py:407 +#: pagination.py:427 msgid "Invalid cursor" msgstr "" #: relations.py:207 -#, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:208 -#, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" @@ -364,25 +349,22 @@ msgid "Invalid hyperlink - Object does not exist." msgstr "" #: relations.py:243 -#, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:402 -#, python-brace-format +#: relations.py:401 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:403 +#: relations.py:402 msgid "Invalid value." msgstr "" #: serializers.py:326 -#, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: templates/rest_framework/admin.html:118 +#: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:128 msgid "Filters" msgstr "" @@ -412,27 +394,23 @@ msgstr "" msgid "No items to select." msgstr "" -#: validators.py:24 +#: validators.py:43 msgid "This field must be unique." msgstr "" -#: validators.py:78 -#, python-brace-format +#: validators.py:97 msgid "The fields {field_names} must make a unique set." msgstr "" -#: validators.py:226 -#, python-brace-format +#: validators.py:245 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: validators.py:241 -#, python-brace-format +#: validators.py:260 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: validators.py:254 -#, python-brace-format +#: validators.py:273 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" @@ -440,15 +418,19 @@ msgstr "" msgid "Invalid version in \"Accept\" header." msgstr "" -#: versioning.py:73 versioning.py:115 +#: versioning.py:73 msgid "Invalid version in URL path." msgstr "" -#: versioning.py:144 +#: versioning.py:115 +msgid "Invalid version in URL path. Does not match any version namespace." +msgstr "" + +#: versioning.py:147 msgid "Invalid version in hostname." msgstr "" -#: versioning.py:166 +#: versioning.py:169 msgid "Invalid version in query parameter." msgstr "" diff --git a/rest_framework/routers.py b/rest_framework/routers.py index a71bb7791..69d73e842 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -273,10 +273,11 @@ class DefaultRouter(SimpleRouter): include_root_view = True include_format_suffixes = True root_view_name = 'api-root' - schema_renderers = [renderers.CoreJSONRenderer] + default_schema_renderers = [renderers.CoreJSONRenderer] def __init__(self, *args, **kwargs): self.schema_title = kwargs.pop('schema_title', None) + self.schema_renderers = kwargs.pop('schema_renderers', self.default_schema_renderers) super(DefaultRouter, self).__init__(*args, **kwargs) def get_api_root_view(self, schema_urls=None): diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 3fcc85c3b..8c39202f4 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -1013,12 +1013,12 @@ class ModelSerializer(Serializer): if fields is None and exclude is None: warnings.warn( "Creating a ModelSerializer without either the 'fields' " - "attribute or the 'exclude' attribute is pending deprecation " + "attribute or the 'exclude' attribute is deprecated " "since 3.3.0. Add an explicit fields = '__all__' to the " "{serializer_class} serializer.".format( serializer_class=self.__class__.__name__ ), - PendingDeprecationWarning + DeprecationWarning ) if fields == ALL_FIELDS: diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 946b905c6..68c7709e8 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -19,8 +19,6 @@ back to the defaults. """ from __future__ import unicode_literals -import warnings - from django.conf import settings from django.test.signals import setting_changed from django.utils import six @@ -218,7 +216,7 @@ class APISettings(object): SETTINGS_DOC = "http://www.django-rest-framework.org/api-guide/settings/" for setting in REMOVED_SETTINGS: if setting in user_settings: - warnings.warn("The '%s' setting has been removed. Please refer to '%s' for available settings." % (setting, SETTINGS_DOC), DeprecationWarning) + raise RuntimeError("The '%s' setting has been removed. Please refer to '%s' for available settings." % (setting, SETTINGS_DOC)) return user_settings diff --git a/tests/test_settings.py b/tests/test_settings.py index 658c61997..9ba552d28 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -1,7 +1,5 @@ from __future__ import unicode_literals -import warnings - from django.test import TestCase from rest_framework.settings import APISettings @@ -25,13 +23,10 @@ class TestSettings(TestCase): Make sure user is alerted with an error when a removed setting is set. """ - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") + with self.assertRaises(RuntimeError): APISettings({ 'MAX_PAGINATE_BY': 100 }) - self.assertEqual(len(w), 1) - self.assertTrue(issubclass(w[-1].category, DeprecationWarning)) class TestSettingTypes(TestCase): From e107c1dc35896ad11ca662c2379754b36b6c9e30 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 13 Jul 2016 16:33:27 +0100 Subject: [PATCH 013/175] Resize sponsor images --- docs/img/premium/rover-readme.png | Bin 50446 -> 53118 bytes docs/img/premium/sentry-readme.png | Bin 21795 -> 24584 bytes docs/img/premium/stream-readme.png | Bin 13940 -> 19341 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/img/premium/rover-readme.png b/docs/img/premium/rover-readme.png index 3436a049c6c0b8deb7aca2140e7784d43461e476..aeef1de4f4fde08476547984fa27046c2fadaec9 100644 GIT binary patch delta 52469 zcmV)WK(4=ziUaRT4 zGLr!VW&|`jJTH?`0~M2i17-^`GaxZGIxsgnIWv>-1A%|~;++)$06+jqL_t(|0qni! z+h*5w-glXL4GJ{4CBmnjXQlv#+PR zT+^)A@7M^eC!}}m(7#E?YhQQ6piHtNnHbgk!gJgVX1=+q#?4_X+Z!(6T1m0qzeXdl zo{+B5fyFK72rcfFZ3Et5GfzD8_W2FUBo|92orr(tIKj+omN`O7o4$-1Q_nmJoj-q3 zRW{kO-@y2F@Kqmy^@McQ58)d)poQyHpv6rR7~4%=iNRGT1Jfme0AEN#5o1~NM780( zC@n+`-4yMZQh?3zMc;{Nq#HpS>j~*<8nIr0*j#O|MRq~dH)sJq zIX6!d@N6g^i-Jdgk)R(^-#bi`*zH zE7fD$2AB;44UWNK@phifX5jVa)f0jBgmk5j6$@}>amFZMwM{l(tTUPsrv~68B`tqu z`rcHcVR^SaH*-jqLo)$P4}aREgYn!?+bDk1iv(HCYZekt(T)2s(5YY(wBdYFaf$9jqu5*T2yKhzW_4hgga?L}N%lH)`LRsy~3g$mY8uuqAq5*IKhoLFIccsqZQw46~8 zeEdfr%qk0Qcm->pTmyjK2qhgeSxjvT58TLEe-5KPFEbnF;Pv*}M_}{Xt6bMOMqpfV zBjkcc@6|NIEUd`cp2nQUD2UlhFq0As=kA2sQMCQ zEy7c5hiX#EdF(?VeBF0Dgyw%!-*g4<_GpJl!*5Ar!m~Q@STcUn28f)C^&#ydn@!Vw zYIx@T_4Z|p!1^B2WedF&*D9#BHCnRJZyJF5%i>E?0WdBkGkI-AuKFY%^H4cK6bvAo z#Y4k;GQ~pzfpZ)kuY>6qG zuVrIfV1k%W5}kP5u}^={MoV$26PJr-1kZSALf4!N0!9W4>i85i)y3!+2-Z8R8G-eL zw3^X;16iKavx)8HqD|n1sD)!3E3)t`N(i%q6Ra06inY9{Y1xLyboaxRE!KZbd@%^yY$JOx<|~=# z2VoAiZM$$W(X!cu2jZe>9%MBSV&E%XR@gA~(x#X<%?Vij^=hdRSWiex4R3^dqK~t^ z=DQ}n*9I$#MPt%5E=V*X5l!$Bm2H-BIUx{GA4=7V)!LKrmJU`N?j|`+7$u#E42^Y> zh(cjLwM#;C?XQ-rf=Q}Hu8G$hfl1i2-8w%nIekhgX980W5LohC^+

DIv}&Pz^MX9z*=SaG+j{|(L<%Lm zSIxL6y!Z?0Uwu}Ec)U1RBx$&|JkVB?#q4(J=+q*#_#=O#dEm;ww5-OGI*BAr01qyD znvdm*uDKL1ev}E?Zb6e$`ppr0dXFiRrvwllZRLlP3bllcC#s19LOUMnI9AApt@<#L zxZyP|H3fWH*_vPuP`mk7fY-a19)b0Qbm=jCa}sKnyGrJm7S3D(3qV9Hp7BJq+T(d8 z;U*bc_(gv!3NJ2FfknV?xzpB56?$PzQ?#_x7f;y+tM@`m22!A*qz=c(7gTLYI!O%8 zS4+QmTmM{N)3?3t5Bm5w;Vm;{<)k_W7Ojj5kHEB>wyo=AMIQ$`!MGieNy~=|+6Js| zmJxyVgtUyP&SmyuV6v-taX8zgKTZEEs?G=0PC|dRpt6J!(0r*aKCE7{+1C&bev?cL z-D(}6S>~2uT9}|J$jD%1L5p5Ok|w}G-+!T<+gZ{~LmR#n_2&A(O~mS@6C0Z=S{KMl z8svt%j-4$JxN)W}{c|8Z)i)g*5|ukJm45X{kY%lJRx<+Y328N>S+YD~d)4>a6K+0{ zX&Zl~O(&@wS_lKIkt^*pI?14jB)8KQOk+w~(3m}pT2vCw9!#jMD7Hm)TPA#PIbn{t z(yM0crfFszYTa`><10CoJcDXe8q7<(553$j3)+q;g5m0Z^NjtHmKXxHag|)u6MJ;U z2oDu7fYc!m7ck1Kc2i767X12lRU)vSkS>2AjGmD5Sy^o+v=6r$N3B0kWNlagNf5w8 zWEf2+i-0}l1q%g+Dj~pM+$|@POOC0NY_f$fJm|6t0M^rsD!e`!ZsL;FwiRr|BL!K7 z_qiAwRvJq-Y|uHOK_9>ZpJB)u@z^@a1|K~@lbTv@{Dz=-jJL#+VU+bd-ZcPyeyV@R z$v7*DE)teRB{8*K)?o))C?qL!T*KDuw-tf)g!Hz;R&$btG|oy1>Nb;kgr zn1;pM^Z=l#KFcpSfE4vdmBxCJHV*bISuZgysOB?g5`$=ZL1PESJfa0|(1?h%*o;hQ z!+{fHqA6ZG1{%HK)tr*p#5?C3^)-JzyjT=PqdiC)6GTpDcxAAL&hsGkL zBYQ(ML<~K4NF1?nVnVMGMFOs9%`nG}fc3`Pjlgt;TYsUZJ?o){+88=u@jNu@KB&_dMvK~n7BeU+rmgBI!e@u!#vU263H6X zj65q~MKi{Jrluiyk;k~dt#e?fU>Vdd0@k>_kC5(F@B4}HncjAFb zJWbPk9o_dTn#r>+E7M|AXG{z&17F+5_6Z$$X};ji3m9O_55d$I&)hdBl%_?Z=*Q#< zTz{kXxm@HlZBaPwlq`SDpcrP2Slf{J30qlizoocp6Iyo>NSA0OV6hf`NV6?_7GQJy zdi#P1tnVRR5R!`nn5dqN)HTBzmPsj~0T5KFekT?1tRcW&JSzp6EyCII=Y-=r%*JYw zf$7B3VvGaBqD2wK3e}#&f-#Dw`B*%#xORh%ZH1;AB{fXgQG9>!MyDEW-;B?67NpIL zZ`5byqZ+_2t&c=Ru?}=%&>RMC41h`HSMh)ghgv{eD)5#I4>GCMDiZY3^q*ns7jBX+ z0(F=NO>7d`XBu^iMFi{RXGLH=A^oiAfUBA0iIo``kY;7W`j-*_la*v+r23wubWF@% zN}Acj$z%tGv?PD77}N{JJ?j!)%$S3GPj!S<8+hX)p|(~iYTE=o0wT*csXy*TVFO} zFiKCTo@|CN>$q&J!Ynl#KC&p_Ja>{?_9jbOqvSy>M3=0l!D#l$p~X=-D9L5(+u`2^Ku zMwVqPsNVpEj%-cK)5z*C_UnHFlq4A#UWPd9(WgAq-yHviiGxFp&pFG}kIXnFxH zqx82f@FQO}R_cPb=`p3-%?Q0=Rim$9Em|0t?#Gr&H_*aUJ^>x+W?X?GTjX(iOqOHP zLD3>J#M7JsXI&P2A#H)zt6^AANN+R?G>JUNH0g6lR@0H0NvTQbbmIw^_0dVlR7Fby zF}Z&>mT7i#x(|NT)f58s`3tWG@idM5bdZmIYPwB>iC?uBEOabf16B7Tp7V6Ob<;Q9 zoPOnkwbn`Q$csX7(9`Bm1^vY`_8VuwqOL{y;>D&CH~Q4R+1}`}8v9%Q#;G3nylC@> zK+(eEM6YeytqO&vD*Djb3@Z{Eo@Sy8y$F964%4j5q?VtJiPtx?2&^Zh3Wk4OXMu6E zCY&dqC$Od-GY$BB!l`f4T6EBdEE5@7qz!0BI=2O_g+pyUrq#v@#1G!W#82I&`1(-6 zG{!+Q@mzFaLuXktT5FGmu@NE`ntN{BCVK#1@s4%Vm5-*2ywJ{d0bhs+n#4HC80CM@ z?F4Tc$!gMhEn5E|bPX+SXe>u;)TIY-P}r8zBW7zw2R?;ZKwl6Mn9FszycfN|BTo9} zD={;^g1DOEJcqvX+Up6a#yopA>P$1x7jH z+OVwF>ve)gCa@|eBDz*m@(bUK&Ru^SBn}Lu%^$`6u_J)mW#5R8CXu2My`it=<$f>F z2yU9#XgMPv-oek7I)4ot<(GdG9a6+$+oB%`c;L3J!EL|$!CMXFz>tkEqmF_Vf5((K zj3P~oEQu(-v2MQdk?zRZI;dNiVd~Itcrhsc7+v33BYY(7pbq%8qXVcMZeboF#yBjlkiN>VIv_H$X_G4ZX z4|GJ$R2vRT2-^J7)?Z>6+{k}sdRa8$i%x>4af*-OY0l_3jd>F>pgkwNXNGG369Qb= z08afh*SCtmJ3Aru+^VV2$$@U>jkZiEDkilGlZ*d33LB$0_k4(LRZwF=A{=7 zFwfdTq8HlIALxY|gKu~Xr$4o)7E;>E@B}x}x4QQG{OEA&K=dThSDSwjt1^OPxcV)( zdFOm-_cj#0gu7>{+C)xsrw@(-RQO4G+6G4sR8tFuC)^slg=|reKo_G37SdU zu&$vKre|R#pmA5-LL`4>K1rc*aza5gWWqw!{jPf|)8}t-QPZC-ALHkoNlz@2K%-6^ zrFTjylb+zpfAY<-SEJ@7aD}A;ZD=3SWrWXrGoC)W0=iWqygsUN%j=6j4 zw@&Jr2-{*z4WkcDFZAEdd@VIyRPp9MPMmYVd7EWQi#h-h#t7 zYt^P}s8WPOQ5O?nGP)X=+NLgo-VwcbY(nz9olOGwJ>)gz0Px>5nrNy?CZ2HfNhKb- zEy@XpehDNo^{?!F?`Ra!c<4))C%gFt=-OIr-~^lFJcEC00gV=6ZzqCS8CwLk0irMc z(_#Zx-dHB;!i)A!Sb#bKrJiU+HOgpN(Suy4Bh?+3=A&;7#)0P_x?>H<{;N=-XM{y| z5=F^uyDb;lDr#pO)UaUc#K8vO^Mc=D3vUFm&G_0viJUBy#kR=8L+&IrpGS~ejRlo_ zwM*?>RUv=hIn_HZA+a#VQWJ*0!Jv8p2+)-IRnt^6fSJSp;3J|1LBNzOPGqz^W3-_F zx5DlgJ$!RfbQ>U+(KE^8O2;+50D!eh~D&y53b zB{MzHMefDd%SME3>Za00`3+vAIv|Sh4 zRy>x2U8P4pFT}x^nZI!%+jhu3|6J z^=yVYCx$XDfW#t_ul4Oa=2>CpmqZ)m8lNBewQY;GVq4oJJyB;Vfg@MCMjC&t zp(JYA24ZjwL1?;AgLYSqhMUEk5p6Q*Vovl}r>+r!7ko)Zs4Vxtv27t`i3vl|OeMku z{o^@`dj~Y$F$pOn-1BWV2{eb8i(bM^)+zRUtDf8wdR)Cw6;jAxHnFq*NSZ zlEU?}t%X^V;I?6=DPl+Z$%1$sAL0|XcWIvhbqJ|rv!vM1=xG|PLVnVNJ*ajWhwv6Q zsVW#W6VTC;xPT-}e3ouy7L}AhzLbA_Up$P0u*2i$M1yF0@{R7+qFjNATf+FSdmqToy{f)o91 z#oKM8fzi+=#)M8_?U+jaqG@ zT1|(<=$YH%l_z+KAM4l?-fm-rWyh4VsX60EFV01O@EN)qJGJPxPmP0ze;6OB**0W^ zIc$O26dbZZdJ*iAF9m<*tM)uOMIWqCv`!0;oDo)|F^P-(W}IQs zHEE4Wt+C;$n|2g!*@jx3th5QS6*#4J85)x=0uxp?K|98!Oo@M#wjjX30;a2lyhh;pvYs8-n0K zpicn-EPSppkiKZW*Fz1l;1Sbu3@y7eyWk77B4h?j6K{M#NVi)R;xAU_{#^`zHNvi~ zgwz8(n^?Wr#)*FbjXFJ;hBs2Rmd-e7m_Cp*K|D!3L+CRZCpsph^_1SMIcAo)kX?MP zdjgl@Owq{z^w^5x2|F;#l-Sy@hJ_sJ$g+QJkJyc&Kf+oQ=0Tvoj14-q{e-1-Pls(q zYk2cbk+T|c!G}yVr40l0$i%1Uff9fC=c2Z0Tti$IR84=rAfI}`yTG0%yrom7JO^w&-!;79~ z9s7dfRd7TOjJBxpzu=(hxYABPxRKVvBN+khkEv#T^N#(2sG=oM| zgyP!UUVB&BGRhvQo=X`-W{k>gK+k&?6|kmedQ1f9v&1~1m}r!)(;E-XM!~HVJ8VlP zk;T=-EY_-!n*|6U?9s&@Ns0wYb@~xo_E5uwkZ+yHqD|@Ljyx7nU~IEk0;dmcM8yHx^F9=9A3mE3nL(6%^}y+m z(vuwOh8ZC)JTFa9RvWseXc+x&TU+r?5}*JBYiX`V?CULbEos3XYzC=fo8|a~kF9@= zqwYA2#)>=f3g0|-w9yKW<3_EQUoid~NgTWg!!<)lreX7j zt;0s@OD2j?!^tzphBN0*3@6W>9L}6SrM)NYJLh-|Bq=2;=Aq*w zosO?M{*kob>fpR^sa2^@bb$eOG0lGk%|c#%)wPk3W`n{If~AEH2vu z1ku8!&ce#9hCZbiZ`Tc{Ztr7FW9GeT&y82R^J@Y(@kQpeHKynp4=4IrFc5!>6&EYz zy=|Q1+Yj*FZhXVWctX=i1&TN&U>sQ~={T?(hE4iM^4Yj)^RRj2)?xRSeZvhq4h(y@ z-7@Umx_{Wcb>Fak%kE+O#_NVHN=92mLn7L=VYB}>kXR}hB&4(F&M6U{8qSKRh^J(JbFI#5PH${pn2ZaAiCWn1*``fLDMjsA`2)`+9658Sj+}B z@lhCMG$OAQPE#oG*j6;6mH5zCo5eOREOb{t@*$mI#tAfEpd+t+N@9P>6t@?G;!plf zn$rpff%?O)vdwVX1@9yidJ`Gha1$0d>r6fsZ!b7%=vu5gYPSW8w&Ig@YZDA%0H$8Y z18K8ZfzJ;Mtl`_ZQHf{MHt*q(d~Vut`*3jA-NVg0?i}`QKQL_Dykpq1L5XMcHtiLW zd@g;7V8^MmC;WHv+!22#rH2oHZ+PPMzjLxWb@uph_WWskjxRXq7&$PC2QE4|_+xa) z1O(o34H1M_Z@KhnUq;_Mp|lCz_W%jVLhwM=q;;Fo78H=NJ=hHw4!WM$Ug(}A5F#*A zng%HibK7#JR;cM~L0ClBMPoZ6B6v$Sy%$``>x2_nc*}{2S>S)UU0H&_T4w2#Oi%1G z&L*%C&~X8-KXY9Kj+9O^p)V(9@L3p2Q1h6(^oWoDWrr~i0xcEeDq&esz-;Z4U}*NZ z8_0rEx1t=IR25SHdl8- zQlLEmJqoJ*qe35CPg3^_3B3CnN>AWWTbL6~4Y(6f4?gVBdGwhH^ynBmwdX(gv97ig zZLP^!0%A!at!1f0-@1ishN>q?m=PZHtKExrf+By&;=GwBCji}#OmrEVy_N_KY?KjO zp~5`VJBc9-VE!7ep36#my5IWXYsqa>1WKf0%9X1TzYwR4;708YZ5p-=d$#W%4(`0$ zf4A>?|8VD?`;?q^wHa4r%^upl`|clZ-*wOM{TKdf_`#ts4NtuG;Bfk^PKGNmi6y2h zY7T$8T8bA5*oBx^F>Kav_|;Nh-3f{D@#L(fJR2lKJdu0g1Tj9-nlYQklNKBe>4n|H zIH!g&^m7SmHyw3hMVp)!*|7FPX5sZ-kr`N(SYi2rn7(BNWGg%IbPdq{70u7BV$LGj zN?5yNDrq7qj5LbZ#f*a%8u79?lHh4r$l`xy4x^p0smfT4x(dF)j7TZqiq{S$oI7+F z_TKC7A8ym4KCts$IuyHZf&6OdT(@P<@R{3wd$@1^FARVC>>mzad-jXkTY6P-)Kx$E zOI#@PWJILE%))1yt95f#CZsX=9-kTv&tA0!1MEXHhDva!7Xull4C^QZd@_bmjf;QG zF>@GiWwV9V5>#6@JV|6Bs_9cdS)F9E*Mlnfv9lAF_-O;v-#k&x`weeh)GlqFWX${H zBnA#m(M^g%Znnmifb1a#&x#35#?ir zl51jhMeO?Fo_(Jh-habqhXdE$IqZMZ6|rTW3u-f$UGLj|^YHmQ|CtUsZyNse$$vS# ze)6RZSB2IyZ8lf2r}yTWsxFS2$qTL(qUQMrXG1iG68@HOWaFq8}5K#C3ntDUYnu z_E^=Fv|=L$KoUPXX_VLHhY6;cT5c4JfcEQT^u5y-cYYuwrn#WhH_10`255Ejk2A-pJcBQiEZlfbZnvuKPlj?GwJRGPZS%$pZ^J1U6^&jfW+nM3dw5N3B zk?yoXj8ybskRzAGzvbp3)bQw8)?)8y-^uy+ve@V zE!Qa#UH^&U!#Dk^F4f@BW{?>5(%qzoRKKXxdb?5i-+0bR4JI)Tc zTJwM!@v|gfdAO@8A(1e87q#Q=Us2+^5PX*7G&W5OS=zN72T^?|ovOv%U3jv(7;N|` zw9{JH+J@E)LH5!h&Yn^BTbiPI=lxDlT?aQQbH5p=RV=kh8n%!TLN{)0S}`!JqXLiLHyjf+JneM7eNDeZYLdAPa zB&Zq@5+|FhWl^%!tjCppBwh?xYai< zTGTsu!#RJYU%d67>hA6bb*J}F6)zrnQPdQ~#7?yX>QR{1hhjF5^OYXLD?cI4qPZ}s zwMyoF4+_yPILp8bp@t)31XH6Aam>$vvEnL0z@H%o0hh`QZ#f2&4j_>hHA0ehCz#aH zX)zX6Nd$n36UwZ%OxtXQEl%XT|aykUsRWv_p)Qo&R_BmWF$-|dI7-q>})%qF>| zCIC^ODu=&CQ~ZY8QpH3aWLrk0CQk4dy^-``6pU(k5< zsDlrN4vDG~k#vl0VJr_-!1b@Kr>68xfvwgFEFxiEEZq;Qb4P2SCx7;7@fE-lzwGEuGoas0liq5jMM7r`-eOvK1_j{ z==ZYDz_8MKz{aKJ)yK$5nv>70_K2dzqOz%klHmuTrBtJzJt1I->x6^Y*fPSwH%))F zfnY*P8e`Id1|5J~6!p9`4p`VnTASCjg==z>apM~n6-g`&OFiGVIf1>gIg4Wux(S9B zPEB98dAIh8J~sT?ZU4=1^Ugch56^nM-k|1jn0N2}$nf-$hxBHFvzZzi3jerzB2|J? zJGZ?E>3LY=R_!+sf7!uTY(nbA;)Q>xFnRiC4R|4q!4?6(>697@x7k3>mN5ux7C+RT z%s}>^5!D&k(+*fc<7aH4Nt%RkQjJ}(`<~ZJ+6;g0=m-oj`049O-MDL#}Vpa z*4(evn@P_~U^#dZ0P@cTgOpzh77C^D?hD6Kgv-gvqY6<`JB!h_K+{HPw%PkZDn&9x znz4^fQ^JytlIf){v~i^y?zVqptYyo>o)JWpGObK%(~Pq28QxZ4Q^D{P`LV8is2(JN zPk4kjZMRLQ%^0etsYxH5iRZ{``bjRnZ{4_Kc#rmqK7Gr7G2FHHgIDyqBHnQn_v+

o;&iW->HNdSmL7qHL%y?uEsboL$H4~23~@eD>osv zQw}7HK$Dtadm7XPFB~v1lq^8{#nbdmAY=eH7Ca4Y4A7<#1*03O&@lFue+@85ZgtJh3^U|r}ehSL&u-h zdyJ0z{vTet#(?nVNS>A3xp~iU-PS#Ro8(&h!F^cozWyV_4`2Fgz3ffTx^=7+O0N$M zgkjb9AP7LDN80AO!s;tGAyxGH*EkuUpeoq4(jm!wPMAAcO<{kZhM;$0y*C6O3rBL? zhrJa|LeV7g)8ZvT^svSRQPc=~uZIpYBc83g@Ke}E)+Dp-Q*EM9I;#rg1>UUGlxDeD z+XUIn$ySo$*Sy3sL9n$QnvobUc*lM0w~z!QQp;|$6ASU_g{_Xm4ciV3zi{9m4fo&t zg<zn(Et)G8*{YS$WAO4?*LnogaPM^^YiRCAUNxQaQ?{_NQqX&B5tGzwmv&0)G z*Q(sL=R-<{*AGW{Rf+9N)$^JP+H&gmW5-+$pRhnJ5l8R>0~8}z10 z&jP!7emH-sJ(C9yeRX*7<*(_+%}))VzwP&Q`1t|tEv?O42Cmd;jI2~2E$TO9r3Q)PFvJ)Yo9iNM~l^Im)jSI zq6k{NMHS;ben~{4A}*uU(k1$FoXW_}I=r}Z_xp!mI{15fGXE26x)(7;;H|e_IQGQw z)7O8#H=I%I^ZxC~aC{svoOP6jpB(!7@aoAIhTpjJUko481HYTs=>Dnq?EUEQ@ZoRy zLjn%GV(w9?0iDKT8h^Z~p?>Q@Sp$wMFCo<;!1uAK2qyD4kOcv3T8^gGhC0PlM@@do zp@*UBT1wfAfsVf}w0p6s_NtX0@rS)@#C3m?>KYm7;D2~pYV2aGC)SmSZrgK@PU`-) zZvfn`^AVdgiJACZ$~`-r9*&UCCvXw~Siz3|Pou2(C?wjP-b4MQ?zV^%)^**Ne`tXhMUv{}!?|sVa zRup?F>mR^ORP{Ys7&}>0V`x`qhM8~Hzue6$Dj_AP3W*v_?w;@+v`#=1zNSjivPjY- zX)s9BU=V2I1P^~|ccQYmp`>I=)lh$XjDh;bN*R>F_p&rCxFid#o$#^UnhoEzW$*CG z1HU=^;(_1QrSAQP%{QC<+~^M7pR-4gO&rv#S3ffR^wsYUUw-=E`reZ>96r=PJVG*M zUU$5JUyyXle=3|Zt(nt?XCC)B1x~(dP&(%{Z^8$L7q)?NR-AFJTyWGF!sx+wU{Ul-uE&j9R8teixPDE?Sf zd=tFmZ~p3=r}QNm$(ty!B=mZ$pdm)o@}Ex)@!n|f{ner|!&R{P$;*G=7+yO0yuPu< zfwCO(_)hVr)iHIPJ>DDj(*Bdf1A1vcSH2X5rF(jvzM68Ke&9U*vLS^5P>V6gZpNfB zYIn(At$HJ0V=ixfMI|JHGscWS>A(B59d$qoFoAX}{oc+}n4((29|cyREo+7w`%#u@ z7kpSH6;rfPCEX}j+vI=P0v@XhH>)QbhheMs7Cx#Q+J5=A-`8d1xAu${pW?Y~_j`uV z-}-yQ?K|(4A}qnf*h$Z}0Sjof2|FkI;G#g(KG0WNj85J{IgZqE(&W}vIFy7Yv7wD# z=>1D<{GdZj9e(Q1!9vX?6yFbIFE2sJn_xYyZ>l|_J<_A6ULJq_b{OQbTy*Ky*q6V= zc(Wc1<__xD^oB<+N-VqZ{2yOViJ_x49t+PsjXh(7;hr*aBy)rb6V5cQknNR~kQ5Zh zi2xEHZL-wUcQO*BVi|(!Ez%VV87iUI)~g1|lZ0t>dYYLOeNy-E%&!80d#eRGFQ0gBIHvbY zbMUH?|b4!1>Gq!Ei#gN?LKH0;p7>#n9 zwc#HMfxQPib7tIAYM#qnUr`CE$DSxUfR1cZO`2>)r3%_v=m~jnoj9`ML^MlPoj@#% z$kPD7Qd)3c5hiz}b74U@5bA>pV$CD2Y<#e@JcIf2?~1L6-`{}&e7r4&y5Y`(-Fz7Sx; z*>k6d6K9U-Tnt~&czcDL5`TR7tHaly{R5q3enmblZ_2V4_SBIFhr@bZET6i@6I`@U zxSEdZ@)D$>Jty=fZ6(jA{gy`~m$+fPw&@e!wo`vrq@K3e>mGx_e|6Xs_5^9W1P@08 zE^ocO(S0j^RYc~;yB$F4j#$=;f=o;WgpEJikkRs@gMc0bK~cY207rR+EL1IWe@$!qV*eWJ%Nk)$?BO>c$kZT(#Dhz+DHPJp{qmvYkE1+B%35h^CikgPhbX5~T zYKIsaO4nn9X^}xdh}k>n&|84;orpXV2rZ?qX-BZpYDYw9%qm3x3c9w4){D5QjatNE z4R`O}vg6%v@+7u7f98g(Rr|Nyrq{aOp@ekCw5=Q4y)e*?eVAcKustxKoN#7ufB=6( zBBmwqVq8m1wwviLBne&UmU*n+`k|G zLhq91Gt}?i^U=3`xcf7e&5+L?eQbD4pSM0M{djnR$@X>h)SQW+NNfDs_YZB`xYJ2;&(<6LOxAxEB|5)@ z&~iMrWhs0W8^_BVqGT^zqob&L5IdG&Uen3pSGZ#JCQtHm68sgNFZ%K`|8Dr|Gyh(X zM!Y=Sbe$5g?zvg!rSy|ZdJn(yP2K<$G1d=Aka6u;!nI!oP9kf^XGHZ z5nJVrbZyzxkE>vZtjQ3mMX7(je~r=yVVaufCM+H(F8AKj<-HGyKxA$ci!syFCcHiE z`6Zu7GX;VRoCy0DN|LOY3m^z;5j!X@Tm7%5(Y_d|rC5c;UoYrVfV@n{|Iue6JoixOL}! z+S~ZB9y_?h4~UY4P7gV0{PdBZ3{SoOli|>bCx>G?_j68PyKupo{u#zs_N>%rj1~)V zXc)Yg#~$@FS9`Wzc;D5W|HRoN!(*@goj#EG2fFv_`#Se@LWicihY#NH%fs%U z!#kJrcG@o-du;g8%U^%d-cjB$G`i8k+AaDuzJTx;pPdZc9<)byQt_EH6k?StbZ2Yv%XV}f-?q_q(Gm=An3wk zFkNJ^c1(xI;u#Os2z1Fnrw>_ZK;|uMX7;ay*P!)Dli?6bp8kK_r_K;s0y5e%!1Oor ze(23N`g`)~u3Fio=c;z;T+)us*XcaVDcp6zJxcnq6VQ$=yNA1!gt+bCUAsT%`=PkI zcwTPXdCzd4zIAq3kE=ZV%6Enzzx>tV*`tpPr|K&gco(l!+rR2NP)MRr=}6Gppaa8m zdI$8Q!wbis7~X#d_^x9`po~0fxvELd}19GIARjJP2}Q8WIckMno19 znMmOdS0B3Qq#!R)2hBs2$F*m0R?m-})}(Us!#)`Vo%1WMT+IQWp zg(Bki;U|Bue0?~1=BUPpUt-C09LH#s8V5-@J8!*-n5PmCO=QNZfh_(p$H&T0>t(sp}INViQh5Q8CzZB9aT zSStaCHeJI&2bk_!ZZA(lnmIf+S?Co9w`y{e6bQ~3$c0ET;T`KrXlj=nFAgF(8x&-) zn0Id7JKU;oeeufFJzH-YcJnF>(H+xOomWo1q|ePfJv{mP!}`TMG@Ra`Nh+CbT_+<% zGlqY$Inw}B0c0`i_+E!e`!DfQKYCUclE+qE?#<&Ei2>H&s@;_Zm~x%I^7;YY<^5T` z7@p5EzuCfxc&-X^r}cS#a_7O9zwW~)qSq7I-s3zDql}IJCl-#s0uITEvqzQKzNJL> zuHl{=KIKc*oGUsh_>0G$8ou|^Uku-V{y%>Yr_SdUADzs$>WAV^y}*q_)Mb{Zbf|hr zc8q(%_+#Hn>olyD^yiGp@>tv8n?Aytlv?<*1p1Dgb(x&x`Ye|0LX9{<3mpgN9bq6IU;m@5c4r-grT zofg^$_5Pas{TRlryY9Xq-WNhhNYGyl*8Lwe8dg^`+QP z-t?P3oO=BAANnxLzYHG0imw9!wfcYF5*z*$6-Wd>r}Hx0Z1|1m|4<9)v`$jruH^sv z@XXPNhKFDMwqpFClFdK!UGQMT!-NQ}W?(~&P3tz&9#Q2~d6*-}k+MaiD zV^I^oWDTd5i4>{^2b2V`Z3}zc_|jOYSHEzmd0aP(S`QZu6!0V*<^%d>Kcs)m4fwQ2pzKK^obi zy`Yckql2G4_&a)2;cc*9v@nr)sP%>&|7iH2-re)$Yd;?T_PIYC9(nx-!%6MhN2cUs zY!?~j?hFK;!z11-xQycIr8$4^co0JxRoKJ1W6%4C&mH_P^&X-NlWx=fyqd$>yY_r! zcv0^Jdg=Jnx-s#kuy_U&XLV_NBDeFMh-6`bh$-hfe)`&Xbolm|uj=sy*4K3fjvF6I z+`dQ43?)wv-Lv=OIuUb`TLW#CK5XmS zB&I(r242u@_3mJv`GWf?NA?SrSsu z)DBQ5px%8X5Du_v(`aUpVmd?)wqV53aApqpos>d$$%F#Dd z(8!bd@7w{;!?3DeWMl5?hR zzU##6BrIEadDlm7{I%hLUW#_9Ws9D>yJPqJhJ$*I2$$eJcNJWGc2ab#t1X|2xoJkl z!1UkamHUf0XC#mk(I&mRLsO72&X_o%y;oN!59qLZ+maG`oA-Z&0BJy$zn;*4l|(E3 znPs&wWuQkFLp_(OE=FkmpE%GI4KX>RvEj<2l#d%6#g%byU#A({<%*F(XYf;#*yllk*kah+$Z3U-;;$Se!m`7;Y666Fr6A1bQ zF67WxAXP)lxt5RV%NC!1zy0^#bTYEC+VJas^JWTpb^riC07*naRCRX_pTFbxeJ*C7 zzN#9VsPfO&Nv#u;?G1CrGq%rVUTT}GQk-u&tHa1#^i>?JO#bS;3!lk(|GrPly44w8 z9^l-+{dR@6cF80;<5aQtYIFcHkc`1B4jw-k1n0`*X6+I2Y@QQ;X~8{i1V&^Wirl&L zeZ!60Z_}Q}@_sWg+KN)a;Ro$V)g|i*1)bIrW7IWci|R`{cH3(TVCGx`l-W= zM%Do)$;OzIm4Z+4iqHxJ-hK$$?J|Du$K^;yn6WxDwj6rPRSjg4intRmPYO1~Z7MAO z1ljQ~o3=!M3L{$GIC!g`{ra3fL42vdw47M|#an+zN$GcoeOqsq^wK27)=ihj ztmYT&bX^4zX4MLN5GT$Y(DAL4M|&h}OO*dkZoxEXT`&rOl|lKrJy zZ9DJH%s*%Z%YVUX6rZZa0$dPe$_WxH$%^w==v=L&x~MaMz2|gi_Vc<&s$Mc;V4JtN zIE88#LC{v^IY3)~S@`F4Az`ncS-i=6e-^mJ4TqbjbYZ|Z;d)GDifWtJ>%-;=W_z)d zb(G>Z4i|uR*tjBD^Xl>>BzZkLPP>Dtw!=9hD}V&eLmor|sVPuF_iF3`K$hCDY~8$l z__Qu}a|pA48il0xsRO??d|HWVw?6w!!a>n^J_)6mST%=M$4?(!4b=-WNOG_0?q{xE zq0oy#I=5)=i2ICg-T5A$zgl3rgbqo8gv`5*nEV+$k|56Nk41pbE*>3$6NH>&X`E=` zfh~cH3@`G?InUvmF7w}_D{}1JE~}8#pV3XAFY4TX7`E6XwR%Zb% zecH&pRrhmo6X&Hrw^-_aM)k~*M|8PBSAn6a`A>6PAHjh1e{hUdjSwBLp&BZ$!v(+1 zu(q4alaMrkV=}2WK~10tSmKvy!e}SKWa)tmtP>gZ{JD5^)KcIN>b@6V`>~pZPvCs| zmfzBU$?<>e`*!BT>+#2svf+#_$?~q{)hy^ct_N0m=QF$P93t@Vysjed(F?oYzwa0I zE4})c$vNM%k%vnv^JKirGj*JJaNJ|nQ(m=ZxD$4UY!(V=88;zV1thf^=lyyt=7t@& zF7@i#3Ef-uw65}TwJ-Aw(|d-+Oj(6>W1(+9j}wE?rF6y>*|*WMo`Ys+GlrkCBsFR}M*a68__NbIAP4YXwx3CjEuQ8k!xeHPhTRcoJ+DiEDa?5Kl66tcW~ z77);c^TKfCoQdEHqIXkkK+LN*{SooI_I^k=;;sI7;5t#^_w-7UUzZkHX}djt zB^L&odqiG3{-jJQ+=XtuWe7JC^1huHPCPO01+k~5Gc!7wdYj(oe8&Axp7 zNk;#Yo2WrRLlH&cVaVR2qNH(h92h5PVRl0#B6@XNT>7TX%Wyls(mD7k=!XJN8rA+>iH2qwSBC1v;?A9{g<%|{FJ^Ts1j`qOLcgs2+bFk4Emp^s63ikZ*NApO+qM2{IcSpO`Zv+K0&NUL2lAs1&5N$D2xHc@ptg$rZbW{{MTP6UWY#snRwhn0Lh6CbuktW z4eVXqwd;d=ht%r-kdzuf`1*|YE?zzLVv1Y85jKecRE_BiMV!k(1rn*tuSy@#b1{@lP`o^G66=0_J# z=rHXu?km$_+lGybq3pnt9=`OjYzCaSn=lmG^7>l6(NmAx@Vs8UgkgE*r>}h9hnv=r zL8v_)X(z6=KJcod?3VX`kQ&{I%VX!^)5czpu_zap=g11lYnqV8alEim0T6;}c! zG8`q2G0~_nvHgeZ9;M(J2bZc7xMpF5SELd_X7A-`u@oCHyT{FQ0mD zc=pJne(VCv+DgxxS$_dnT1?whYV_~?z*l+i(_=bY-{Sky-?H&y;M^g%D{$LFY?f7R zi<&lXv@sgSHD#xNvW7<3%m|~^U+X=iHepQ@QakBAeVk6{Yzw=M^FD#;n0DP1;EZJu z#(`*#VENU3NryG3^+>?73h98yGCrYCLvtcI3rpH!f)1$kY7gC^%ws4I>9aa#F5wn{ zGn#}?=uwvMyznQw)O*4wv+>K1jwm_p)q9QDLs~}Rf#aipXAYPBNhGa@7;2DVSJ$e_`_8q5;l z9f$(C5-}(H-=oKo?$iyW+_JFj^0c0_d`53XI3r&u@k55lptjvD)>Ocz!`|7?MQDqm zePNCEp`T%Y=3KkYnkFPifS@Q)9RTMPfbL95tiEA#r6QZ9cLM3=wQuOj`6pj{(7(zn$$nm+J(Op40`r@C7K}$Y z^29z7BsRJzuR|U7Dt7CYB+Dq=q0Nid#^Mm;oNUN{f5)7Sp)3SrJ<0_Q*5fT)C~N%G zV-M@Vj(hY?v*q5ylba!bQ z*@cKbq#w9fPCTnLNPHDLj4h*At`9r}ogaK=i+kZIxJOAF*Px-{KQavjwl-yL6B5qx zMHj-KhqVIUixaW2xc?L5^8kQAXklQ4BJmB#6GUV zkd*%9vHxxOyO+MKlikmFQcVeF(K6nb&E$LX$OFSypZ((SgF|2Pp;~+#Bf$%?&A5rj zI+jqlU*`qA&*(K@k?Ld^{e+uQ@yN)3j7;rJdnX>oNX2oPM$hz4VvbCV-mQD64(Nu? zOTD8$(k{M5CvPn{a`F|;A06y2*cTq33sWXDj{>(QiTJw(0e$$1^B zJ)-kpkJKT1%>(?<|M`z|uaCqzbXZNxFs~nY0KiW}mDiA)3!Bok09!_9c@GMIt+3XP z&YiBd$rOt*mncFG4KWgs>7CL<>WS9@LWUaifk03PohAB^_9_mact%%l?kG8{UvbE^ zW7GAex8%!+wj7Alh3tL$=mWzaJ@$f5fPQj#kDg}d8L{o!)!(#f%dk;dm@8?VwB#_4 zSHC`d_}jiBbeiYZ#-uZgg=CX|)OecytX|o%Nx#^uSI#>bap=oAEP~DGWK?8>>}A}T z{E0e|XDlb@Gx?`6XJlvG<@M*|DE3LW90d!&%@M?7eVsJR5&Cd-{DRMzrTs-o^UQ&&&@vFVzy-SBqJf-AX zHxr^wro`-6ivA>6;5|dJ1dmeL2mNq<@P=O+K77-!tjW0}R6nhMM_s=E!e3M}A^}#E zae$DjV$LVJQ{BjhBdg0iRdpnFAH4YG;l6#J(v^^pj+7I{rMH;4$5)&@?}a#;+iK+G zM!lFFACKxA@SHGz&f_1OU31lpli2yaB?a)VAed*8#+ehLya4TpKD~2T=SEg5A+guR zOVu8JcOrcV75&&lx^q zxlk&W3{J0y8mJ;mVx>cipM|l_7>J)kHy@F3gwjr%N*l}X&T{J&o-Ici&QkjLG|5PQ z%F#|tb{ZPOMS$Ga1|k;%m$vKK1UHRwL=|NOy~_sOk^8(psq;lo#)htD&Ee8BTD(m3 z8f#V4BtTbx+vCMRU-t5J7&icR((yf0lELdacxf4XMLGXc62#+BqR0M9s%jS7i#qAd z7emmy8ikKEa`PaUmw9_5dugbQ{VZafh#%HRAKf+$!kFloK*zrseaoTVxBYm1UOn-WE^s`i^KjF>RM=$ua75)o`zHTFf&Y4cL_~LyfBfdZH{8DK-ZeRq&6qxN z_%+i)57L-K?2W9GXr?OYz`B-e-+!uZBD{)k$Z1`k14kdA{Tpz%hH*ngqw8l^upF|$a-a zV;@`e@x~-05qV}sQnrbO1(u(qa5Xp)d{%Zmp%dmmd4&rc8b$$Qu7I+h}LtJXxC#f71?sy%#x*NDw`7AXpr(ZQr!pFEr!Ll*=ky;e0`R zdas>+NsB923T>ZFn*RV`w}(nLPIJ~|vUihiCAn+Q$A-`7J>`4+%wT+4mfgtZ7Lo5g z|L3|h`$7GXVLLik%yZ8PP&0ef?jLZ%O(7`}{77&wqXMn)UWY{-D)G=~z-FR)UZ!1|z`HkJua|e-GVIZf zbejfURnr7#Dje6BPWct&9n+kDSK6|PD>t9!Zpp#~cpdx(7!4}Cd9tktr;jj)B2)?s2{eqp?KB?d&M({m@Dv-+cBz4No2Yi4?|C zCt%b|WgZs2akyvS{liCZ{B^x3?v`Pzp4Zx}y`)g*;aCzIdpH~_y>?QIN0+8~wZuz$ z=g})?UK)@gK|uV ze2>2RgW;Pm{Gsaa`!<5+>qWb=E+;IpaVPDh2f+7e_i(|9q{W}Tkg-RvG+B-82$$@{^jtU7yrzE&&zSAahrhNExK9qpuV{J zG2WY`7w+%SH`0D)sh8Pbuf=)Co_mKgdhym#eNf;DJw*AqKC(!H;z|D_f^m`=8`!_{ zP7{LTpm1k~M|J+^*b}b}_v;>+yYvhtYZ^UiXZ3XiPB7;LT_R4;A=&$Vy_dan` zW+$VF?ienA);-bZG_)z5e9~h)!b^4B=ty@4Wb@dhYJ~9%o>Q8}`a5+ny%4 z09N=?P`e|?T+e8UfjLTCw*i`Zfyaz1!&Si5tCpXCkftd3c+^g_AqaA~cL_ZS2+$au znlxsBUI=E3O7D^)Is+qk$?S-w+aXs?a3!N_KHdD4r~aSe#UqamA3yK~eOcoJdYRkK zpZnTWKHu}IzOG6#`ug+#X*i|?;bc^{U?TSuv3vh3xBs)@eK-69n735;73W;j^_ytm0NU&`}U_#{vUqf|9yKu>GL$a z8e^*-b0LwjP*0paGQ6alMxW5VM&H%P8o31ldFdQ-i#f7(JZVF`iyBgnZ2au&6>jJc>?nA zm``vcL(ix+XRBT9die=y43tclb524u^_M*y5mZ1NGHj`sDB59r_#XeLSu^r$41boDcZxs_f11E~QQS7$fSrmx=QaM|CdZIen-8 zftSCg@6hYyxX$%(E&&%QoB-rbX+9hDOSk^MWd?I;TNVP}x977)A2|Mtx&ron-LUv$ zJyw!W_?*_cpG;Fl&4Hq~K_^6yc+cm5(O19ggv6f6j*Yuz<5u}}N{5Fp>CKZ*>Hei> zeZusjdqo)xJ1|cy@d(co@2O6#!8iRpadn%g;(5Cz3x3WE-)ffE^_d_ZwK%0k-Z5&6 zW1qA!$WLjA{%_F}$2H{daVitJtv%eUvL9|$udMZTu(n7KPgoNqkgacOr z=#17II5yBogChOq?4`3b$;b#z4G*No$x)Rr|pmG+|dRlp1hMt!cdK& z`s0SV-?-yn=w03)n+?8jt5EoGBQG!8uMb^*;D*oXaPC39tnC}RrQy5!1o(@2ff{z$ zf6US8drjy(;r@W$HlVN13dcWikdjdSr+&w1!Boo&q?5^nZ{!&G38mJ5M8Akf&*sfr z^{uzNm-fmO4B@I2=hR3_oCfg2&lMY@<7p$h`J7<>ZPpLb?RweT=WqLGI!yc6njNO` zYLus5duaI2I-T&W?vC$dG}XX?Qm3{E*nqLuE0l_HR2l{PG?D)o}OT zkD3nDG72{ExgGZE-o598!=am>9Uf6q;%xv=aW9hY>h&Z}2xP*41|LSAKaWqiG$Izv zb3EpdadMZlbKFAvBJ%iQ8E5qhyt>4SPaW}zrDYZN93R$usCY4 z)%p=U?f5-O2;6;t^q8r!Ra@4KZUuTZyGu_k||)YaFy-^;Up{(G}K5JtkA!7#DOWj*L+V5=-~zgHS&9IJzqy z_I##s^T<^MB(L1;q|MOvT67Jillbyt(2|#)kUTFP(5TF=bHY2$=1L5T0#kep6mfE& zVA8Gy!X6Q?3gt4o53hFJHC(^-ChZ0E+xqlLVDz2qjwpRsU>L1jD!6kyB}36}8*qSo@WYF^uMr{2l5e~0!w z^!cZUbc5t0I(NitLx_dWZp9h3@U3IXJnxA*!bCGAClgFQi_(ArShA~b3o;9m z^+LCQb>nr4*X@4GrAd~w;n}sLylSLwhD?&oTtX%Di2IB7Xzsjs@BKgj>JChQMK(bp$aZ#`C<$6cfvAlEVIa=?)7JOi zaKAruv`_CcW2ZJ=MGv_6 zGi1@*<2?aq1lXm~<24i?+y84iA-=p1*GA z@gpVNhc(4gO(v)v%-GBo81C5D0L{li?M6-VDt;cTS-bhF<{Z}b+i&&><7IvblGptD zyK5SMCnEHhv6)VK)1zK@a;uW}z1M%zS0fp}`5c;A&N+NPFM$+IDo!&DFQroh_w#g3omsysd zkYqgzC83Bjc1kfJEQYYD7`4OA;}dg4pos6#m9S4A{D;FmdWZ2Iy+^ti6eRP)rFd?7 z+%@RZawQ6$IXiOd72kNsl?+Z&zi{+%AD(UWZcrIvK{CptKbNX@T!=j)=cM?5?K|%s zK5*mbM7}nMYAY}_HhiW=U@$JiwUjxm7t}~tNWy%h4%cqd9_2FQ#l?r0^rlO0HszpBn1~rcP-)iBbk^J4DL%q9n7xPm`bd~CV@VABs4}YV+ zapi{uEH+p65`QK{jK=2Dia%;^)vIGasB=fl`X1U_^>I$WS&4}g$GerdZ`JvcAH4Ke z+FSar9#!E5WyCpgcbs8#=-WX6i;ICccTSV&Ek9P5KzYvflq~ z8?<>&-@`kmll#2Vgtuvb?$D>e_inw#x{1j_ee#;S#Ic7(y_SV!^uVES41fLXztbBU zemv-!i%M)@%VC)zsuIcy=!mN|!QmDZnV?mhRh!w^vW5UNcvSj}&*}GhBCoU3=tL*_ zm?AnzLym~0mE|QQM^<*16LySt&C)(z#j{R6t_h_6bwDgefnnl*+^-uL@6}@%m-=hQ zD`)rULyzy@`kCQYeI@na&bx>2>BhjPbs3%KxjbBDum?_dcY^ZBy05(>(cE&~UE1CM zuqHj%udckpNqRoM$R~Pk(3LG-_3@*Z|31BVPW-O{VzIVeNFj@y$qEJ(^S6 zW8*fBFF*Zn^1!T;G&7z#=U7(TIYwj=R zQ{fyk@Rf`E5B#R~nhp+stJjnKP>Yr4%6cMBq+{RLL*-ajsk517V!dSJqm%hD54P~O2GV$qu?FSBjO<$!w%501)8IBq#RqFR}FhHQk zz5!uqwh<>U>L-dPH)~*E{ zVyW|&lDPIB5;bv5UsSVSd5bWA8!a_RIu1JB>LWA)L=hM){GrJ;8x{79{LlJ?4bg~m zV~LyPucnFF8<>p(%Mdf|9Yk*s1#)rcsV=x6463SdjAv@Rzn zTCKy-)!i)pkj;@LPV%gOk5O?wflu8&0!zE#6(nAzy3{CG>$Kq{_icJJ;BK93Ii#B@ zxv9?j8BqDc>sD`Map?xTYoU;o-mMq8@h)!0=E);J_VZu4;v&!Gv&V+vpF`)Q|SM7|#)QPFw9g|fjD&XGjH19c`2z)^Un>Qq`s`qaoj0MU)E8BIY#)GKC zM*Q*M$(LP-u16!5|g695JC$)kB+eS4lg6Si3g z#MCVAr!AO+4+BYNqU|-3B!gVxGf6G3*N-`b^K)#B8{?vXGLBP?qhSb;!Pc!d>(o0E zmMv&lqo>*ywu+mjC8P^%w3mWj%#jY{1x%2i_dOdtvCkEuwJO}`$hXlbE6v-cM8*4Z zuC=mVhgJ9JA=6j&{vaN)c~!6YW#V3H;cjUT3wb^7wN}`xd{LiQ`qAOP8Gfw8v*+|N zfpfgc^CIVe7FR~Hm&6#83jo5vB)D>klF3xVQ@MraMVFNkZ)H=uth3wj&)f8|b4g`s z38`&%UBSc=5{!42?4&mS2}vfVJ!Wo2yW{Zukw^Uz#C&;nNs(9~7Q4Ah;?mAwuC9{o z*}Tuc_A6MfiSAC_;r#x6pBY}!+Y!G1(wF=N6s`+@@#9Gt7!z7OHr+PZnBX8Vy=u+@ z-oRrTdPU2%RJgZ@4o)`x4}OBzS8#!3C`6Jd*5;`RD0Sre{pU2eU!gfyCsgLZX<*wrK@ z4UI>CC%cDG)X+uzczF!!+R)gvFvE)2qn(?@+j68cADWa}nfg}X(Mnp`b6 zL%y9@@ouFL>777_Pdq<7qBjL_ZiEHU6Wj4Xu;Sr$Fb9-E4=W^^;fxz^(!5p*C#`v} zInO|nh9?d$VVM z$*$|X^I!%+48+7)6eSX(Xo;dElD6DoN$z&YlDZ$dA3FM>ANtLIlV3WbBNUEsOJQ4X z*$lNLOQJZBA}JD_K!PApm}@}Ig+dkJ@ArLc?XxpcfC!@U)~%|$@6A1|d9A(n&XbvE z;8ajgb5*)crv1EY;2X=T+oq-7b zF6!NFNR#oiqxhc1tsc8KzUnWVQhGRQo(`^PPU}03FF*U|KD+pr^aKd*LA0xX!mVl_ z)1mkoeNmaSwV4=MBzw3)KDYL`)(hX)@vwsTocJZ*)|XA1-x_=FJU@&bh;_0(za1RK z>rVYrdB2X3Zo7&T8Y+tqwqj~D-YA03_1Bg=Dxx2=tu08qMBm*{w?iacs0 z8w>D6ulgEf;}K#MHX6|lT+0Z5_gFs=jgrl)Hz;GQyX^@t~2G9kg zfa;sxT^sG_aLN$sKThM;)eciX#tLGPQjjmchdL0 z+vRUEwp#n&5Lw%Svf&jR`tS`hHVDQ7f>M5y@_{i9tm%EUBW%X-!pExx(x-G=Juw)f zsxdW_rsWO4*P)M$5C%;ys&Un>^vB=G>jtNYawqijPfiroP8*kDY5E0`8VHmhJAUvaZ>g}d8Q zWZ~*>x1tu4z^$jv3m;IgKlEXVz^LH0y?%RxLgX&W&Nn2-4!k(>5CluO(&pWu z3DXjOg3aObaXrfVtIz$#^4CB4ce;4-zxX>JZs7m37QPeUb{wC1`cL(!?I)xopJtgT z=13nvWd4nRetggMv>|aJEZ4g|ppAt*XAa#G7LAqXnK8ymowY4EPn3Q7^pAW4|7Z0%pZjgUTTgi94v<_~&PT3HY$>#W zRC%{}Mc)&h)fbjOULHO6_2pYfKcmg)Cu8C=AvI-Ah?{(Kv)DQbyT-fs-}WBe+2H*; z1NmEjezcSV#8i16nHq*&80V~d-FxX)-JwyN689ay+6zzE{rKDeK)*UZp@)V(?VMx- zAq)1@pB10+JyHd0jLly0WDW4&!`gV(^cO>KOXjsv~CW#=1GCQC+t%Yosvg2GhdQ-X_A>$qnGNWUA=_U4qG zKEdT^iPQEU(UsL)M|7_ehbKvJ-)!!qbdPSC`WF4tbF+?k{GE_wa#}ynwLhUVlAk;C zxW1QqMEBSJf#4irVOyp$bA*K|y7BStx8C8C`~tXsYHoJ+PH*xL_&H~lyPi9$b-`4B zOkRFPr)J!)+r_>^AKNbf4-Z!&@?^vZbT9W`JN++~FFf;KmKXKRyRjw}1G0ufEWD36 zC`ORlA|Oz~Lx((bsPZ0FLG{s(0HPK?InRb-4$=5y90I~^xVsjD0_33!9$s)#xfWP5 zLk6_$Amc;rRS25^-kc;O{s`ZU5exf&WiPwmkTAI%aF`Vk{yZUeP(1=IGQ;ROn2w1m zb5$VcNK^<#%SOYy@)=zSdsatEJj?wSebl&1CsE#|GmqJjZo27ay-)K_{jxTr7rptM zdg-V)rI&Q7KSpNCN}nghMybLKE)cz6Cq&-n@%2;@Umg+kfNt~4Nq{%{nRa7;nrfm( z;|F-MiE-``m!n)Og;OCus*i3@=*^20B6B2|^mb+@iQ}#}g)PsWQ!A14&ACrcFQL2^ zyaUkBre#sA8^MD|EApn(4SdNsC3vXzX-AxIM4J!PWU7{ihgG_?(jg8EJ9aOvD zklG=&4oZ!)DG`p|2=Fc&6>Ua;1OW=QnH4$+O6!RTEn+!Zf|@^WL3>!I2^?OY&}fs5 zao@|BkA4&;Dq>A-y+CUo`FdLqfxKJy47&a{B;s?|t@rC_^X>ZO>}G#IMBE&knorMU z%?;nRg`S(ST{{;b69=AD@`<S}&1=473a9gEj$5#ShqCBr-O40c84 z2HF%GG#X@6qD|hz=81rCE784Xz)NR7B0>@kmF1DsM?R1`=S79QC`W?(v&sN3f4w(f zYGZdIpuot>Ek{=X?+6`eLc>_tO7zBTgkbEuk52!VPv7aT?Ms3V>xY&hs^ zq_81Bq$l5e&Nn1H8Ix9yIkq_62m$B|2@g`heCp{fFc zpu=hK8o5U0-Nqw{SSK=li?xWsSrdMHw5$1ljAMNISqn#$x7>7VPLLe& zMw%i=@EpLziJSP}s|$2lxVtK3%i(i}{9^_;&L4MX*pmhnZJ;Q3AfRd^kMX|!=k+D{dqlFk zSY5bye)+-iuPk5GIcWFk%kz&t`1|@5>^Jpzr`G9{{DE#B_^h7i_vbo& z1Ul#z1Zt#L*hVd%7e8@-EmPd23fVwzwiDC!XenvM>^6L2^$B=km4f%R;5)M#g1 zNl@c|URp~S6KR=DOii8_8M~JCqi@35noOc?6J4x|BkapAEeN+4AnN8M4= zMOX3Mbki+tv=?n(55Zj-{TQhsv!;pLKxf=*?+-ei=2NoDakKMO7bi!_YYk@1>` z2(0)+>0Fj3Gf}AaIE*x~hBUe;58 zR=KeB{Kc~)KExvL$m(SBbl%b3zIJWbSNESef83`aT-3E?FE1DT@$Ku+{^jyFhyKVP zh}@LS7CEDK(BJtHl8=I9c((}Oa=l|_`HHZu|S_1HZL=N$oCFUVd^Ok{+q}DfoldT5>MPn1XACk*j z@B9dPF1}p1wQV8SMf=La?OKn$^t_^+yGP>oW_*cMuJyb3w%vYQ zdqqb@PwQ?BJYl$}hn8OgFK{`LZld-n-KOSShd=Ez@^?K&ciei99`*iz`~Ilv1x->W z1xJE*J|$t3;UN>Iay&5&YuDL}5GIJ565e@UIotG>Vd}LCTi#mX=B+;F6-UrlCc!Wh53Fc^r*G zOL%mbe5^=Ugz!KQlZxcH3&S3k-ET>?ify`r23pKfZ#{c?bcuj3PWqBUa) zX{021@+Th89e9%_;MtUrK{BKrA_fEKm-Uh5sLuc&`0J!|{QUFF<9b#b*Z%b{XC0cv zk12S?kICkKQM8^Uot7R!&u(veaozw32EUeWb*hhIFjeC}y&O2>A;De+{LUwPoS zm-p!c@0;`-zdl#XUuru)V_UX_t^SmE#I}d}Vxp@9Ck*CK84UR%S2}Hc#Z4 zMpXizQN`ZCY(8}Ju0-%Dz*|dC2N=FRI{DHIx=yQp-i7!&D4gH(#EI`MkDvUGJ`U-c zKLumNj09Kw`kkCB`|sDswT|1a!t>l|?stAtde@JSp1q#nZe`n3x*G%!h4xM@M-CDj_Z_iJNPmVUFvOrfCDAja zrY)0J!F8}Zh_s%NIf7Oo3BV+m$u~`y(glv1@R_E6Gzw}vXtCp*?zeR6{D};&lbVMe zeE)?n>WJhSLf|qs9ECB-&3QRDFZbxnY;OCq>+;Om$9x-O=G&WLx1MGh)vXNSJ1;iV zM~;2Po7gUGkgWa zp7^HkgG5Z`_&H+s z&n@3P@+m(+OC+N{lNwazx_tFyY5%lGu%%Re0XJAHR_)G;8k#BXjLD6KYb^>UwX=D+K!`;VS` zYB_c8sMK;2B1tn%0rTH4gEMzrRQ>#a=|j3T@!u>*^~Jn-C$`JoefwK~UT1Otp-wrt zOKB4W9W(Ms1xV+d<5`D;;0CG$JsX&@0u>C-o=94M_hD=S zw5w!fO)Mg3ESYLMDcqW>%m7z1kaVFoq;os|)uv3iVhxu=eEFIGx_s%GKhsAw-5pV- z7f>7JxDh}9-QZi3+{fJ^bxFl8%QI(wyqwa__Fs8f8xnKP%X3*;>DRbsTnOxZdHDdM zKwZDl@vrF~?4Q*Q{dW7Yn@fy-<^JDZe?Fowxo>*;CTCvfk*y@JSo~#Y_mhhqy%pb( zruTT{xki#>Un>^L?7&$&i9@15M&}HHNfYp46f0-ezlDL{yf z^?+p^Sb(bHXE?9H)o?JoEjvF`+vYO?1eQ1o&@GX0pNJ$jrh_LR*`Ve{QQ#cK$g&(m z!9_N~b#xtAgcAOhAG*GML$`2y!mtniDxDW zb&1Q9`a=DL^;;GBSvs9_fAS+6J-H~m%a`OOIlPvhgFy$c(Bp`P1yJ%?Th3^WGix5H zABoN9Rx;*xLur()M(nV+CmT{9HYN;loX8WHz!ENpjVP)F7n+2%rb1{aJdquE!fg@4 z&=SB^@;|1Z)=wX|u_1)01^Up5NoPKP1_ROO`&*-e~ zHQqmuCvNfEp{QtNo;0Yp(c5&p5$=7&xj4Hl++6?UONW<>7cb;UfQ8!=h+^i@i!bv} z-SbV4IVLXOJ^H2Pe<5A&amPJ(@AM-kcNhKeeZRH*;OJMD?;QPN!c{iMHgXWdVjDJk zT(%aeeF7J+=+(Nxck6K)v5l(OCo;cH`h{X)6xvL_o7F(0KO zHaqFQ;As_91&2Tpz?!eY?g?NtXx6}uAl9blfGV&xePTkne`>R2B6|XCBM6nA9QZx4 z@L)~xaiZW4^-zJ2zvK5!eCCP1<(D&5B$>0CpF4Z zHm_GWGSYWq7j*drH>|&~==>_aC+^sZnqt4*%S`lqblmvP=pJoGciyV!wiS=bcTjPp zas0)6p&ebae}+7;ovN5G>n&Sr{;;*C)|5wel=Qu0U)H0Y-s@ZKnq@~7U-p0OoqynK z@1E1+wI)Mk?wW(P*haSP7$n~q4zGd(V#Js$NEDF}hgyA8qr;FjPLZPQfS%NVwp(P+ zeN+M4qp~L(((DT{#6der_Aqt;suiF=u0aC{M22;ce*ujN5S99-CBR(_4m_Yzrh(@H zo~ALM@Bv6%CTnhy;rUnPU!abBarvctKcORo-Jf?8`#0;0`o8B`ognz#qhHX^@?Y20 z?oWAR;rgzmp6ELqx;==K&LRM6?3gK8J?yU%i@`9Y5EIBU*xhH#vX%u{@MRQBC^ z0KzT{f7Tp1_q4u0I;NgBTPIECtQQ=9mBF#Ke8jFyEf0jqX#hVw{?+A|@Be3dip<+~ zdX$81A9&lZFaP}f|F-1(&NLn&M)l4_Sl%V{fI}3zo&qa3WQw@FFYv8kpziT zE4m(!wpp1|(ZFGlDnjbP+;2U5)OIHBk-GT1e=19WI}Q#daq1w6(2GKCc7IVg$&9cV z2ea|uEK-%}8jDN{b$TA6$CHa(Sb_^E{f32J2N6PY0q3F9-_xP^mm9pNEk{K^Iq}`) zub!#4<`Fhw=n>?j zf4rjakGOt~XO?kBA~Z!!vylZQZge}=lrl1S*77!w$oNgPuZh7#;Ia{}E$tIc}_ zM4mRG(}pd{ktC5OsfxDfpzcJ;jV@l%8QaH}hmU?vi{x-b>`~2kSUi93Gf)1>@`&!3 zc!pbWXk*ANIIx=6+Bgo5tKjyze4%f6`=)r?-E7jt3%py&q+Mgu;~V_T+THpte~tTS z@3Qdhy%R4T(HjC6@lY_fe6?Szb>O@(?T>7=EAl{XHe3DTrX$HGPI7(T(UOJC-4*U+ z`OkDSw_nuJ(#`rV&j#8DbjG&W9Y63bIfoOx3XWrp43?j9P9&Khx2AzWMetQZttn^) zgu`+WDIP?1NiG0y$lkPjyCH2kf7c(?;;I57{0I<}Bm_aDrS9SKmfB^Mu-1$GF^*q27e>$?h=cp?mDa!}{3!=$@T<|Dev^ z{+;*!@5_7M`cN*8ltHnxj34TF&86obt@T;v*)};3a);Iv`wt3kDok zjS>y@EmuTV2d2vCG+?*wf8K6L?Ho>1G|oq?1c&vIF#%BmY3yF}0TZT!Jij|Ql1vVP z?MXv{I0UFyxJ^>Hu!ILHc!Q@aO%MRCR}vn`4Z7FPiRJH~|8M%4{1AYh7QE&*tzXfF zqL1kNj|&%hxTUNp`)nch1_(mESlxaZX6MQq^#@Lz{$2*Wpl&n)fA$x;-mD4-C-=|3 zPnV0_t#goeS=jJT=w3{x6>pspsaM$@>#=Af4jE$+G0>b(KGF3CrC%VApMTEhU5(37 zcIkv<^8>)o-SuH@O8>`l?;Y>drljw}6xX(|=WAn5?c?NcCfG5^BzXpFzSTH3$x`~s zc%neBYw;9YazaDPe}z9X9Ey=!I}Xewes4FVQtaGrBPuW?N^eF9ohG4zMBBlH9K1^h zj1~bK1!i6d29bWID&uW7D2y>_l03;0?#3gEKEGByapF76XP*8K%gGmyg?UFcH%R;9 z(|_ihj=iMsQz*8-V_}O7O)REx#BS9iMnLrH0MM#TEFL>_fAE0zWIzF5+MD$hl6!A| zr|y5W+b{Nc=qDHX@;y?FiM{lPdBggiVH=I%daWfcH|!N%IQ%s|^f5P{Td8)}<0R4# z>bY+J`hEYGZs_*&z7B9x8$MxQV!Zl0kHZl!vKsmbCse`Y2nh*^1(aZdEe)L$1SS(;I0*3quZawSZ3BWW+}ia!M?UXME$Hu}oYHMizOLJ&e)I6(=)TrE!A>^i zT@(vC4DBy%qGem!4+h=0i=rF;RNrrplG&S&=*GLXe-$AF+UR|&9#PImHXe_(%fb=N zbElusQK3#vun)wZ!kY%|W*p*H#X(K6Kxp&ZCmtGlOutS(uJ4k@Q$s?v)0!{2KYaf` zU;gF${?Fw;{UDIA$0HQ?+}a2`6JIi4#2=@MuIErrnyOmM5w7Fp9t0|_iFT|-V}vUj zhEun&f4gt4$c8j_-uBp$k+U6Cf}y^_rb!4B@&q}73_N{p5#kw9G@q)Tun|j3tx0cm z8#|QDReBN<@ptUB?);wmAme{mlnMvlnXtdzBUkHg83JTiO8iL}dJ zpV6fvM|6chr#d+9b`k@-;G3+#4;9aoX){=&am-gEaaC$0{1&r@bn&WMkf9dxsi=U}0cllC4mQ`4NF3^YNSmLz84y&wSlRXEwh&+cE=Gs4bmL;9_E{jba2x4pTKG5C1$ z+~MWxx^2c+pZ%mSnMe|0hYh1rAAdw`f3v6f2o@{V()9>N2-Bpmf$?g`M8GqRMbIn> z3nN`n%NN?-kTl#;VZ-2Zk<;3kIpqT1z2THiQ7gYU;*h!OO}nO0F^V5JL%b-Q9>M*R zxJHg6zFl7&9IdIj)aFCFcha3Y7wSt-f6`}=pQ$&UkTe+@=Z)hAvA#eq}D0XDobp#k9p z>&z0(Trwd(aiO1fVvokm*-pno?yw#AQ(rS%z$jC^ za9gebw|kmbPIUeL;jid%s<_aHzdad=QhKj~p z-5yp~bVF*V$8KE7GW%~DDi)j(2`D@S7ib_N(Il*8>U%OZcH0!q)#J^!7cFfxF_}zM zTfNeoLb{n;&X-{$IdtlWf6GHU=i~#rX6one`jAf`Tsbb?<7fS2XP?n^PoH0Y@WNMp z?Hih>q z3VZua2Aj@(`f=c6x)kZ5w|zn`OD)GPP zmIriF>fQF|xQ?hfx9aSL)AnF{c5$ClA*g&_0b?DS>dI7CZbO=))jKr7cDU5^8Zd;a zO12;dMeDBmWMF4ue`jR#F*cnlXfyxw!b%=LA{6 zWvjTt|Jd25{rwS+JJz&zbZw(BaTquO6PEUU4IMOo(}7V5c_kDahpA8qK;sBx`YSd95laPXU5CIzs(Me$72A-! z;gqXm^O=G|a0Z;9l35)j+Jspk4TLkO0y`q@z!y{ZO&IPmSx5sPDUsc?$CyBDD`xf?;YJz{%>c^~ z>kF&k!s-8R-MWJ>v>ktNJ1Sh^fA#_ogv3S_`;#u9T5c7OjtgyarsEXe4hpe}ywv!AoJ^F8bn_uFCFFyLgr4r(tU?5l@8P(KAODij#d(~XA4Qck*eO0s_ z6bH;X0m2kc5pPr#9lsL_7p!9&8CiKwx}?=Qe-mpDeJf`tZYOGwLl2#L0%oxT1(G&$ z(r=L&@i?59^~>Ao<#~Mva#&ZHv&r0`BqFIO+>eN4gU(4OM*M=mtwXQn;cLah%o|lW zKx4bDBbeeePr-~4JecqRtLUeTHY;zcrUM?>ojUjEPTfa)*M%>e&*>gi@d<6QWyAr* ze@0BU@I`NE(b1>aMYY+y7M}(G*zs@ayRrLzrtkhN^fFd_lzSszvP1ipoA1;^2L9>t zkZyPK@R85SM{-B=EvMacPT+iduWmw?c)Ti}m(ii0Le$=VSbi;oIz!YtOb(Djsi1j- zV!URgr4Z;2DuM3J2~xx1HIF0`{0g*}f1)SDLJyA3Ecjr{S7MHnL$2))IjZzl@Ps(P3`6H0*2` zio*i~_5(p~ZDMZIjr{o%_Euf#zw7dx9$&;Sm+=X0_@EI!e>o2E zFR=IxjzUWuD5;BQ9=i|rM%@kch|Yrlnm=+2aMhN(Z+n~WFz_4kS^md&2P1(Hz%T`f z_0@Y64zuR2WZ54ZQpbRnL{rN*{tS&$MUFY?3}ZPZ(8fbW;EP8j;p@&|W>N%XJSZXo zNJ@`8xzj@5b48M8L2q(k5%9)sf71z};>wJEXKIktf@$q@+w#m5&eZVWa~oC!)+;y# zRHJDD8&>{=3~kVd2R=$e=?81SJ1eKozpxz9Rr%aht$mz*X~pqt zjf2NRHoWQF4gv8AA8|BqG_UBcnO|F;(Om#8>d5tKExfHgzzLV*?M?p4e{cVaHaVq; zd5#@}ntJyqn!WDsj|~a0Ss4UH5g{nvsTnuiP;gqF1Xm~+vRDai^HgNB(gxjFiH2LS zgqh27EUR#xv+H7)7U4kmVmGH@u>fnj4C`PhbBx z!(U*=tE~`azJO!@1h+d45XxBjb6V$Kk3L2b;wqKYazuZ6STZcd8`(XF2d`)+iRoqsk=P*a&J&*EnieKmResS;0 zZ%DPkNt(_S0+z)yQ5K$HtvIKL1F@4u&q+$ep_X8PqvqM>Z zFKCfxVMx;qw;U@RRZBOZ_^cMdB)=I9!knq}W(Np-vjKOJe-Q&F0MJX@2Q}EHPV2Zz zhA|o#s>)LBATR?=bJKE*j*#xs350hbaHoa4DDW%fwm$^krb&BmvWAs4@vPziOwKio z4!%+bj6sEl>)Vd&*UpCze^xiV+s(C!sM}?EQ9qzOcKq8?p$x1*^_pV+7*@{bjYgy$ z&|Mn#Q^>yJe;ZQo*0!~hf@Vz`Er^CUP}sz?77~Fk9w5eDH*l|i*h0=qknC(RLUM1 z1l<1E#Mo#*G`3r9Aavayos!3RCU*bEBuOL@u%kEJF*Q@C9#N#PT6CbYCBbMy!=M@^ z;KIX6f3f6})C7fT5)FF+1jE9}!Zru`oB=Dpd(E%5$M9va(pqK>@wYK%Z@6uU_O62u zf#rg*9ckvxvw7Ozgb2gnPaH?Qkuc&`ld_$VOCvdz=Q|R}R(SRGL%B@1^C^D#UR^Zu|vMC9p#5H+S zK|@wkI=fAQjBu8MB1_`47){}Yz(xeSWY-vp^eL@zSRq0dpIw$Cx`gJ` z1$_^qU-k5i0{f^YHoJ8s$^0XpJdFmRMPrtMb{sx5qK6^kDo|Avl~K7CbvbbI93 z-!I?Q11Nl3V}~enN&uNx`zc@h4c`9mf1_$&ZAcYAhlhTT-D>P05>%Hg5C>V~rti&w zt%cQ=Nze?4&uQz#6V7s4R*Bqb*oY!Ei#9Tb`Yl?9z3^MA7qRTfxS%$om~D&t7F~Fn z>V`^#9v810gM(QAEFY-}WFFCQhWQ=(m2KC*f3O|p z2_t%!&qbv6AjV_==1^ln?Axa8CTrs284d82JpO@fdc}tmTEG6>UoD4D{y=u@|I+|o z(B&^2)t%P;-kp1%x6I+}T7_%FfAIUE?5hpQ0hy8{;{dWSvI!aP1(fl~p&*h|EI85d z&hHkS7ooSRwO@E<$C@mRiWiVVUd#~CxHY*&n-F`@^;-| zY?r^Vy`&qloz&40cZ$R+?8c<_XT;y0R8{ubbb-1ZohcUPu)rcze?+Yqf3s=dqzhet zxP0}QPwK{a^(5a?x8EAR$o|yh|L^jXt0?O+`9x#g!MJWzgL;e}`iQ%veT6e$IYmPp90pgy z;s-=cBZ>TRmNrjh-DTmfn5WL=w@&* zW4a9E`OoUElJy90fcvO$66a^1{L|%I&wtvFiP~5nveP+^Uy4EAe_-1AedI)$vi~-u zj?tVSwMeol5!5D9OJB6Cn`}oTwTwWFMfCzB^?K3KQo>9}LYOskWZVj%6MjzAY)-JS zVKq+6ma*vz$EX1FVrolS=mTES<4)3Mboy?f^$*FZmVi| z`)Yc9S$c!6mEv-df1RJ9t(%k`ed*bpEv;C#jqMNiHf)5por*V!QxU~}A=L513IGfN zfzNY>h}OlC;Q5!%E`NLI-{*ejdM4g}Dtw9krKdi*eDTnK(n+NHB3q7atPlHK7OT(Z zg!>!*7kWQ5`*%YkVJd07!&kH%O!@>`1dwutf0Hc>Y)x=hV0fXg7pt;l z2{;P-6eWpT1Iun$@J*N19hj{sgzYKtutefUza8urV_Mw|YtaR3{!Ydo!`f{0TNLaa z7y2fu#XsPRh0QM8xghvvT}l2%F8H+UH&~t0Nqwhv+uELrwjVKVf5XbOf#?m{gb+1D zIiLH5mq7(6PzFxONDH&I=;YNO+KlG=|-|2g!liI`= zylii)%d16YywbFn5)zuWM}&0y4S{Q;dV`y*|Vf8zTl=@j$KlYHA)ANVR4iAB#NLl?pJTiNZ!NN>X1ksbt110QNS zfGn(RFr9oP3`8CHs1?-NTlCsRl3bY}i!y`wG_VW<;YOkVBhvy`Ef#B?q7fZpEZaJY zQ82_=MYXdI_Y2wZe)o>1Ri@b20uO>I-DVVJe+}!la1|YokUx)`ownIpP1_F!TyC4v zR5R!0>7UoFMh>5Q-1qXaHKnSiay3x!wyo!27~kd<0dEc_1`bTGj`3=Bn23vU@Ti!V zRxdv4KcmMn{pC;oAN`W`!t(3y_*c5!$6b)^S>YqvR5z6vlfSFE#dH>8fE6G-7%Hy<{TY%Y$PCqpoj%t_Qy?$pZW1wj(C@EZ3Q zd^s>_K1r=)L_(#aRfWQ-X%aRe^iq(`7K9Eu)n z`E4-O49BkRaq)8m+M7RQJqKZdDKHE0ycys*#vqR1{@WA(P7kjWJOwbM*sPot(O8WLQjz?8(cK6!H-U($yw0;40bJ#z-9&Yc@RFVpN2=E?mHZ zOyg`{U}noq8GEYRi>CFoiG^MpiH~N1$_)b=ehD=zb2EgOkTwLFoQrc_w^F^RJ6i6l z@X$tXC;PJQ8#SL=V4qyYwv8RPe~sKWYRlJ-p*N$JC7zyxO>z;8Ieqoq@@S^dJoz7% zA0PjRcEH8&W$zCypXdfIS(#yHpl!Y%1a*F_M6tW@;x{f9uf^aM6-{ z5SS(~C%x?ukXdKak*aQrH~@(W{y_qySb77YH6^;jRQ6dwbeqB&G8Rj?vLMXQVb9Pn z2<(g}n7hQs$ig*S)UEV2wt3ZeqpBsOy+KDU7yU#PK346b@bmR^y2AgZmvck5;ZxZ% z46O`j0LPq4#eE39APrJgf1J)nl2TJ+vQl^pvcLJy^wv!^*D_?>s~h^NbYJsU?}91XA-ZKf-XsWKbf z{p}Xg3AXXkjCn6df3rL@YFM)}H;I92&!I;)B6I3!Xq@|da&!IDy0P0+I-~m=&;6|* zYs4KJdAI{NU*MxeJ$U~7Y{El zy`me`VG(mG8F0I{dBFWIn5ZC;Oo2kn1PVrTHI)6mAtm-}e;jKvI=Bve2ip^&z{4|7 zTBc65r^}NSftf|W`+3q@P^m*smzq>hyx~;L@Ji&7^tNr_(+8#mGA`}R7jcnesZA^X zUMTZ-9%D0V+pq*4Y-y9ag@!&wXv?Qxc%z!O+R#9ZOp#Jm;a9d3diLD2XC7PLf6qsj zJ8yhjqb^I!f7!;*z4*8u`NM5LCdF+dS|;$eQkJ3Td5TBF!n!h#*ornAg?z0(6X(2O z&a6b#Vg;RooHi~p_*^kWjCBudaG~F ze?doAr}ab?9t?Rxo6hrGOngok6Khk}2V?!pcvhPfe_C4Uu-K~SJU2z<7xoPf+2no$ zV*I|Y_w|Ov;+tbRc78%ig3wZFGQ8bX)^zgF`e=&$5uqE`nkab4ur`7}Q{pOn z|Mm9uhO`~VdF({1K=(%F2M3ZBz&#l4^{c!2`uSq2`4T#H?}Z3-07ViOEPY zlFX8U6x@x(G_RG$KiDz}$$Yfnm0uWpKFC#`A ziqJ6{PJ|_IW| zK1t`$$GQSWCjp0h=FK=Xn2`L2LNbfTe`3&4tk9(!TTL6#RZ;Km47Xjo?-_~`=RxK) z8;xf&TLhEMaLw0Dvr|Gir>ti&n}sDZ!7aZh-csPz3m7RNnn?3V{T@{^)R4`=8w6bN zFpdQG+ZMyDcamgX)+!G4B;%kpm6?qiIvU=z!IoxuxpkKv#Ec`OazA}=0jV)V9;hLtRH9@? z%^Wy2G^R^J6^HTB2!t0&YjCl!5g{6Q5Xl?$Yut-qjPapymKk}}B^rt`7_aLof73g#fU_{O+l(J8QsXi9)L zos}h<5IFKduN6f(XgDp&1>+g_WFP=e`8hZ0#o(k8KT6+Q{U5 z(uk=xvjb^lz*CkKVnCN8Dma)ux3 z_NgDZ=hv4H-uoNNyYKvA{@wY9vwWb@f zGn07bcVdQ+NSkTWH?jFsiLQ491Dqx5B{z^6aE~=|e{7pZYBg5MZky%B& z(SU!cFmHw}#l9d9maDy7wHwk#;F+kJB*QyLNILqSNKK`PJZT;au7Q8KFiFyc+Q|bN zx~*Bx7`a&n7H&}xZZl{sd8rE@DilYLTPP^qW`rHUSST*lu9A!mj#^~#5Zeks149+d zn#V<1f0}B2iGQLo31h=rHgQUynsbOAKl!lF!TIjdFD&=H}#?OLF4nD8A zOT!Vd7VL}e3W87?M-b*Ca3Z&Yj78&oe;U}xN)+J7#aHK&j<$X5<+)rZ=l>FJc}^(`gssqzZ{_PA546Lyv3?(sVuH z;E8-#EXh<7x0Sn_3j(5bLD@7x@MtaeP%^R2i=ucuHnlB|-7DJ|o2~@fa+R@|J;ST?oGHGO>mR+M+n~?EqWozvc*T)9KCv6GZA9d zuNMbw>3BH&%_S3hK5Q0n+b-=+B5aIL;VVb6-N6dpL}M(P7B(p(7vV97`nGA{Azl7UIgQIFuBOpTo; zC1fNCa7eODoI=7umDE5G1cHQ6E&UpFYZ-xg(F|f(3r>QHwHJq2tj9pnLq*k_6Yy*d z@TmtsG%C>c;$s{*!UHd98pf|8aAQ3%ft77Y^`>6Ji=%~#PBhKbH0Ti@f81*b8ksYa zRYPPsU}8NSDQ#PDQP)^YYM%*O8_=+lL5vz_RbyPf5i^sdU1N`iXu}SQ=V{0fY;{i3 zEy$#*YTU!t3%~yCoBJW?_$CGsg$U#uCUvn$wH?X0HzTC@P1YS%Wgf$;wOqYF`)@_u z8_hH!?rIVZ7i~5pT4pxmf1YJ%n>B4xq$o|FBtf8FBw7_xEv4WVMg^Q#`kmmR?OJ*) ztLbS~JXBInA^{_f61X(0<4p;eUd0W`B@6h_x1^BGqtzdX`H$t7ly8BTROJv|+ULk! zxuSixjegtI?MlD)jAMas#ep%nX_{7fLUhd263^CBe5;D|;7Kqvf41bcHE+X0zUCN= zi75O~F?slhoaAPr6c~Dh5f8R|<3R*G(>O=37E-d*Zvg>cwe>Z#ANiif(5?OE3wI<$$7bE5 zmOJPtJ7?Rv4}II;KFzIt5^=YPqmLzt9pjc^X~N-mYrq%!;mNE7mpBU5MLfpPn%~Nt z8~rBe79;dR8)Z{h?20AO`QP-TZ*yWpn^vPW77=|-)Yr_0f8^PySzjl8O@MV0$qMFN z#A<<0`KU3=E`^kfND@kLcvSojrhe0raMp<+3xo(-bO=ywwBGG;bZ8+5MB}!d` zU1dPI>Ipp*f5>W^v8{+o$Aphlu$aa^iQGITyZa)T;AVuWV5?V%Y76bRtBR%jW9!S?uQZI<1>wf@E<36YMaIwc;1T;hvWdYqUCcdgXd)`r9kZkL|SnpGy> zIo`>*F=p;pAxSk71DKMR>&l7D`IvR*OMZWTMyHrEAb9q8-=J zhsH83A3Z}6&TZLNW8hfQ_~OMnWLY-8GqOD5=27s~Up?t~hlV&761tyzPl8IR294kDM7~0-oR{HoPlyw^qZ3H9ynk(1VhO|9B5$Uf8RIXv=ujNi<*ye|)nKFw(a$H54oIDBTV{?egD1mlt-iK5q3bJiMFiBhtb26HU@hsgyS51?nfDQJ+({6@qmE$Crt`!nbTBdax zsj6E4>PA33j>O)iGOD`hp|iYHgJjbtft$YBMQ@*0V-l8WJcMoYAWrh4V~yRmPOCI; zdPB5_ThvWFT4jb}XX%46hJ<~Kzih;+e_&b=#s;~~LGHPzAtgpW1v=`U`vJH{I@k7w zRFjk>ufa%!N(Clef3+X@sKyurLd^RVg|wpAP*%X8t$rs;f0Q#LRLQPz57})@T9ZI&!NFsSs(KJv;Lyw~e{=%y z??Gsz+Z3i@gKPTkM^ETcl!e&i)d$NJbNkdSJh4TVn~lobX89id&`~5Y9cm1mhQlKZ zw6f4@++^_&oqlwll^VnRX%+Y>r!i;kDS{v0$adq~tiw)^j zljlUJ#3ffqlFk`r$w(65LMN@6e_(H2PD&E4%V0H0Movyhlu_32q(`w}>YK_HMC#ap z>Q5TOuv!2`!@4!Act~O=umD*oY)V!V9@JaayjcN@H+*jd!C(;=^1$aPNqxhbrsTva zVay)uMQC2zMZe;Tt~%O^ZRl)0(Pc)_x9)BsT>Y`Ovei~qKktq*b*bK(-F=p+qxQF zx-^V+Xb-F@(z3{7MiRL-e~Jrx%08gbHBa}D9ugt3jRsAnYJ}`)Hs7ITV$vuqZoG|P>)N3Y0wtYC9@5ptK`v0Z5d%& zTI{GA$6GAA!L$W)9@z&_U_EFm@s$5Vj_8^u9M^VzZ66_xS-r$07OwJPB`}L0ge(FnTbEe|7siAQ=Gh$_c$n*d#-EzhMbR3{sGd6>Bku zO0U7gxQNKFf7RFUm4#bE*5S3@yS9&zUSsz9U1*?5kV=kDe1lxEK%n&Ut4*pC(TFft zVmjZvkYNd)#6y7-o@6%doVZc|qdM@t4e?v9AWLj#O%D^Z(`s?}$_(g1Jm-C9sV5vKpr#UQ3cckHwX1R?6W zF4y)E(rZlZy3jiDLO9?`3Ko+$5@ioLVjLZkOMko@g7F`;7E#G&yqUs>1QJ}YAqNFk zBDmV#f2<&NF|PVZXH0QJg+{f=H86_-xIv9C95iE*H@##cc5r&ru>!%PBeI}FPYSpm zgU^xE%C~t0gwN7pp3uT$@n}wGwaw$iscBZ9&;bv2q`$Un91N3^SUkb813o*I)^l7o zl;GG>u(r=fICdWiCEGNXSP;QW#}J7fz+^`2e}Y3G72(=Ek5d^ojjyNfb-5wU6YPXb z<|a8v90(d#oraM}@btT;Q*&ex*i}G{oWRxtU(9KErP4WOn3lL zDcRxYwK9Q>Sof&H3B7$VAFz@ro9MME<`96bm^zzIbmfQ#n5hO2V}k&zWaFw$dB_Qc zfAxf{md1)ncH;~Ie`>tffN=p0T64l*^d>Y@2t96Dw0Hq^_kjS5 z4e5gmj~AEw;;4XNUVGA;(2y%+T#J131A`+0EICq9dTmdbtQT6_BUnc})Uk8e2C!(O z^flsOHc4t7v>i+JGZ%rihB9{WmDL z=jHm|kS>)7PM$R@M$#y2ycm^ODOv>T=5V~I8fAi4FOoj&&!X-H5os%HWO+PvDQCTq zP*`%Cq1&>`Ms=zihdf>yY|&=e*7O1?TJgk^7MvFClpO68tO$c^{=%tgHwJG|Rd3Qa zs!a{HMs5VLz~ZB!G($!cO##F+e_~kVk&~7q` zCbS^HF|7w>jlgyJrS|InC3=-XC-S}8t;0n2QM!AqVqd2eVD4im!T-VfQn9d z^Q!JPnwk;eSqRQn^Oh{KSL1w)DEiiEqcmXbK8id*eEOzk(8UO&A&Qp3e>TS@R&4E6 zA2lHHzd*$p`pF~sW=S(5I;gN0080DdIEW~21!&Z$A2;D)gyNVtJ;Sv#0!Z=Tq zEzTX1TgA!K31phUjUzc5e zj^KxKh&2#hK+ywt^?9~UIM5Wgs)pdujnG&P z^kGO1UwYom)gSPh+unp)e_Os%rq^^GN92GR@~YEgA1$>$Hm>%ee+d{Llo5f04QYfz zolf#8S@J!A0K$>GeRM*8lbj&B?n8UVSvg8Tcxu#iMhj}rPEykdW|>gHTS`3?HDiis z+72ryJ3t}Tu%ZjzgM!Nn$?1g$Ib61PmSZOhZoTQpH~M9ty=Q|9CJp_jFByWQXl!|+ zne;V%aPX+Op(-Bef7=$=OLx$sX?f7p3vS>YJNqr1;nWijnBxjhNjD|#h!(u_!6Re> z560>;D2N(tY+YtvdCCAd4$2gPgAHj4hVy$KJBgfEhLZ0jH1B#OV3IsX8_rphn~n#t zsFQ!^A1NK`;7n=;$bxNFNwTi7V4y47D2Eaw)M96sK_{S%e{6|GI}Od!V}y9|1TgA> zug)~a#weftLMjFcXWGF~V^Hzd_JwGo4_@^Kef%sRj6@4#{tJ#;7l3(rj_`}IOK2H6 z#%lY>))b=499g<)><&E>v`!1KL~;Tg=*U6_1HHhx1)yTY)T+B~f)HHax@K^DPY7+!FO6;J7lcGb;e#xOT5pzy^RASwkaMFEtY6)A8pFzCV0 zd@(X`f1y)eL)Xk_Asvw*del-@pQX|P6rJ>AP9I%#+Z$1DQXL*kE}j?;B>EEfb`H6L zV~43GzF@>zq#H*WLXu$PD5TQ4X`Fnj zoa9PU7iBXEoyD^*hipV7EHFz=uaeuc5J1K^e+-!#`=ex}Z;i`9q>9!uykZbBjayzs zXHiKz|E+K6f!`}~wZmq)vIg~vw_!tm;d>m)=z~SF$7TYKag5M_&0>ldcC;MY;%$4q zNfjP5C=H{JYl2w5$n0lyNtVYLWD*D+F9#5?RA&z77VD-m`7)qe_6+zz#}POK ze=w~z*gRxS&>@2hKLMEwX7rMBjB8P8%o+l`E;J-jinvl58N9MUXu;Pgt0jv|&|U>Y zukd}uV1@9`FnCn3gx7O7^kq$b9M(%&S2 z4$A9C;9x^~{rGHRle@_;CH5=y#{d8d%t=H+RGx>b`d|Tola=fSsn^)77z@i9qMM_D z(0ME<@BQ%i7!C?>u%TGTPI&2RczCHJBOMfq!ZsmUg>f6A;rK`gdfa=RQd(t#YP2^U=^A4j^6CmalHFpP zI?ZG1s1;DqwtW&~TH$=uGBKL1c_%(dg3CsMsAg;OY;K!)0laimZF!b{4R;7)xH!-oxiSyz@F==n&ZKi&X?)`h-}sRhj9 z!jTyUq3O~xgs1uJc=1_o@j_=l(^r4ui}|a5lT)eK7i4;{ZY1#`aJ+Php!5Y}2#0M{ z-;kDN$w0#Ed6BZbNx&#Rp$Z$x@7@?VnA`?hsycx3chSp!9)%Mkk!kpTgx+t#94mXQaZAE4#B9j zF}6Te+mcqi@L3mh*qG(qeBgjXH#R->2VZXnqJgLR5Cz}nn8jc6k*huh4}YMKO^HUs z;v-o2y7kbq&51n$Sq5?}4_#3ae)>Xyqs9bY0|sA^AsW19IH=x41P(T&Hxay<)j7Fl zb&|QWmgKD@Z#q!jPiLiZ?i*hVNPV`PF6puGvh<=Rv`}Vyn&eybv{AGyF`_Fx?$l;d z+E}Q7RndmFsO>=Xhm*Sa3V#RB;ER!>K+4LO%}cO~U1{_tibP5_AS6N76pUg~Lwq!f z27%FIP41T+5rLGbYvaUVNzl^wz{+w|i>lT;WB{zv^bCc+6iJgFw67n5gAM8R>YSHyVa^g=5plP}V-~1So1y_q1KK+4bkw!CA@P9OBlP3#1_|#d- zJ{oBm5@cD7twRC})_S^S<#r#Q=EH{O1qRK~>i){tvXK=TmZH8rA4f{TA{XiCD%;&J z9?9-_IED}c6FPjwgDzNjffrN9+*(Uc2%rI97DSa{!UX3|Rp^?>2mLn`frAa{4TbBC z5IeP#oR!Q@YQW0aB!7O%D_Bc+LbGB7r}0uihQ`PM5t?wgPuH|*dsBip)U_Ckj;16> zrsP5qdh|wWOdA@)Wm>C)(0uTk)_nBJNB5{Aqc^JNYkf@?eJee5=2W#>m%R zz_ETjrZ3xKT*(k!Ol*eI(YnGv9eWK@BFa#J9>*@F|d$(;dsyS zvdTi~#pzX3D+rqAVOeoxK%w^HqSk*e%?WCldCbw&*=FF#k~Xp^fItUV{#qUcja~U7 zvvJMSn#|!rthMaHqLKRjMYnMXQgM8$&Bjy`#b6$A-hX5!E^sg-lUAk2Fj>ynm{!_Q z9@M*xz`=&J%jj;&PSUH+K4Gl8Y#a?%H|W$Xj^31Lv*=dAQ2DsV!dZNwVHvTFu<)D5 z5+M+L=0RhYUD1mIK1$jWUa*F?B;-YAW6U~a0Bs3Pv8~S>-UNzLP3(>xsE(Cok;l-q zrASa8K7U%+crmWciE(dW<4EXIFihx8OWlLAcM&+)koGRtt0oe@in~gFO8RQONHppN zRc%-_5Xy_STb3X^MrBc29s(`97b>j!v*_@6@e0r?^6_eD3cZF|=%zLd;}9RXO$rk> z0oMy%b1N&dIc)HSAw02-K8)~=k71C3P-Q&W9)E*_7>%?=8 z0$LRxRiv9uN9;FfwXz*-Y)&P1@CuoY2O+JWjflQ*Lrw`tF)+xxPYXRS<6VAO>@xhSp*I?q&*AurlML?E3j9a5Mz;ntp&nD z1`)n2YygEWCgB0_679^xkp{)GT@Ow}$3ljWKeuKnL69|1r1<-)B0*$r4^m(Dg-jI9 z2eE9H9NWeSK5umCgXiBb$mzhYmycdR7k`O2n4*D&5=}LNCt!;)lufB&E5i&q=wHDI z9BfEeF!&p)r&Z!^jlGCejg1DT8hew00NUo)7!Y;yu#b2#SL=^u#p5Fo2y2I+>!TyL zk?RenHVyQ6Ly3wKTy$aT2!!ZgvPAJ(8z;!zeV6+L)vdMf<0bAG0Y1~l|g-2QLBwb$jYNJib`Y9W3vh< z!hq9L1*u;eAjd~LW&{qhiPL4%urA?1L26xWCXm8HLjafmiewxm6|Oj}%-Jy86PGMZO9P54(Z)f^Bj$dCMr4-Xz#d!jny}H@Ij~UHIlTpI~fm zGj6q{dDace#(0A&ocRks6um|V{ax6=HWU@&Qn2PZXs>7l4mPAK8vdV6x!2sZ@VvfM zb=y)y7ebCy@_C20Y0WW?Ab-5kfP_UTAIa3PWk!k6taQr^ZNp7x9)zesW6~{paL}<) zS&p>LqZ1ymot?vvE|v4;QjI zg)p>i2I`qED;juzWa>2@TF63cU47)^fp8G(HI8Q>HKXA4i@vv?L4P-GK&=mvV*_en z)3rKg|?^1DE~fd?@0HC^*qUjxOa z+TL`=kVHAwYizz5)^xNK1TfT af&UKlGyQg`%qwF60000RT4 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 diff --git a/docs/img/premium/sentry-readme.png b/docs/img/premium/sentry-readme.png index a03cc8ee272f0b4ed61487697134f16197bedf02..0b8a80c496c30ad2466e2d9a4fe8c170bb457e12 100644 GIT binary patch delta 23710 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 diff --git a/docs/img/premium/stream-readme.png b/docs/img/premium/stream-readme.png index 619948d6b566302cae2c56e01fdde1b3bfd456e9..a04009d7fc0d1fe4fab5aa77215a94d5150d6354 100644 GIT binary patch literal 19341 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 Hn1%cw??H&q From 6f8a0f2a68c8132b5173720d3354618d488aa4d4 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 13 Jul 2016 16:46:50 +0100 Subject: [PATCH 014/175] Update release docs --- docs/topics/3.4-announcement.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/docs/topics/3.4-announcement.md b/docs/topics/3.4-announcement.md index d3af44298..6dd668a63 100644 --- a/docs/topics/3.4-announcement.md +++ b/docs/topics/3.4-announcement.md @@ -119,7 +119,7 @@ The following versions of Python and Django are now supported: The 3.4 release includes very limited deprecation or behavioral changes, and should present a straightforward upgrade. -#### Use fields or exclude on serializer classes. +### Use fields or exclude on serializer classes. The following change in 3.3.0 is now escalated from "pending deprecation" to "deprecated". Its usage will continue to function but will raise warnings: @@ -128,7 +128,7 @@ The following change in 3.3.0 is now escalated from "pending deprecation" to option, or an `exclude` option. The `fields = '__all__'` shortcut may be used to explicitly include all fields. -#### Microsecond precision when returning time or datetime +### Microsecond precision when returning time or datetime. Using the default JSON renderer and directly returning a `datetime` or `time` instance will now render with microsecond precision (6 digits), rather than @@ -145,6 +145,22 @@ and `TIME_FORMAT` settings. The renderer behavior can be modified by setting a custom `encoder_class` attribute on a `JSONRenderer` subclass. +### Relational choices no longer displayed in OPTIONS requests. + +Making an `OPTIONS` request to views that have a serializer choice field +will result in a list of the available choices being returned in the response. + +In cases where there is a relational field, the previous behavior would be +to return a list of available instances to choose from for that relational field. + +In order to minimise exposed information the behavior now is to *not* return +choices information for relational fields. + +If you want to override this new behavior you'll need to [implement a custom +metadata class][metadata]. + +See [issue #3751][gh3751] for more information on this behavioral change. + --- ## Other improvements @@ -170,3 +186,5 @@ The full set of itemized release notes [are available here][release-notes]. [api-clients]: api-clients.md [milestone]: https://github.com/tomchristie/django-rest-framework/milestone/35 [release-notes]: release-notes#34 +[metadata]: ../../api-guide/metadata/#custom-metadata-classes +[gh3751]: https://github.com/tomchristie/django-rest-framework/issues/3751 From 06751f85487e0f753ce59e4773e38c622782463a Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 13 Jul 2016 16:59:15 +0100 Subject: [PATCH 015/175] Minor docs tweaks --- README.md | 2 +- docs/topics/3.4-announcement.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7be1b9880..a8e1afbf1 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ There is a live example API for testing purposes, [available here][sandbox]. # Requirements * Python (2.7, 3.2, 3.3, 3.4, 3.5) -* Django (1.8, 1.9) +* Django (1.8, 1.9, 1.10) # Installation diff --git a/docs/topics/3.4-announcement.md b/docs/topics/3.4-announcement.md index 6dd668a63..69343c75e 100644 --- a/docs/topics/3.4-announcement.md +++ b/docs/topics/3.4-announcement.md @@ -77,6 +77,10 @@ the benefits of RESTful Web API design. We're expecting to expand the range of languages that we provide client libraries for over the coming months. +Further work on maturing the API schema support is also planned, including +documentation on supporting file upload and download, and improved support for +documentation generation and parameter annotation. + --- Current support for schema formats is as follows: From df33035a3c10934f153bc5a43042cf78a3dfce52 Mon Sep 17 00:00:00 2001 From: Rashid Al Abri Date: Thu, 14 Jul 2016 15:28:28 +0400 Subject: [PATCH 016/175] Change incorrect example URL (#4261) Changed http://example.com/api/items/4/.json to http://example.com/api/items/4.json --- docs/tutorial/2-requests-and-responses.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md index 7a12cb5d3..5c020a1f7 100644 --- a/docs/tutorial/2-requests-and-responses.md +++ b/docs/tutorial/2-requests-and-responses.md @@ -96,7 +96,7 @@ Notice that we're no longer explicitly tying our requests or responses to a give ## Adding optional format suffixes to our URLs -To take advantage of the fact that our responses are no longer hardwired to a single content type let's add support for format suffixes to our API endpoints. Using format suffixes gives us URLs that explicitly refer to a given format, and means our API will be able to handle URLs such as [http://example.com/api/items/4/.json][json-url]. +To take advantage of the fact that our responses are no longer hardwired to a single content type let's add support for format suffixes to our API endpoints. Using format suffixes gives us URLs that explicitly refer to a given format, and means our API will be able to handle URLs such as [http://example.com/api/items/4.json][json-url]. Start by adding a `format` keyword argument to both of the views, like so. @@ -202,7 +202,7 @@ See the [browsable api][browsable-api] topic for more information about the brow In [tutorial part 3][tut-3], we'll start using class-based views, and see how generic views reduce the amount of code we need to write. -[json-url]: http://example.com/api/items/4/.json +[json-url]: http://example.com/api/items/4.json [devserver]: http://127.0.0.1:8000/snippets/ [browsable-api]: ../topics/browsable-api.md [tut-1]: 1-serialization.md From 5dd3d1b5d9dcbecf960e484eba9945964418a647 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 14 Jul 2016 12:29:37 +0100 Subject: [PATCH 017/175] Update coreapi version in docs --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index ccf7a7cd4..f64056cd8 100644 --- a/docs/index.md +++ b/docs/index.md @@ -91,7 +91,7 @@ REST framework requires the following: The following packages are optional: -* [coreapi][coreapi] (1.31.0+) - Schema generation support. +* [coreapi][coreapi] (1.32.0+) - Schema generation support. * [Markdown][markdown] (2.1.0+) - Markdown support for the browsable API. * [django-filter][django-filter] (0.9.2+) - Filtering support. * [django-crispy-forms][django-crispy-forms] - Improved HTML display for filtering. From aa40c58381ed562aafa1969f2d23d90d89f78191 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 14 Jul 2016 12:33:54 +0100 Subject: [PATCH 018/175] Note 'coreapi dump' command --- docs/topics/api-clients.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/topics/api-clients.md b/docs/topics/api-clients.md index 25b0c6145..f17f5e4d4 100644 --- a/docs/topics/api-clients.md +++ b/docs/topics/api-clients.md @@ -161,15 +161,17 @@ credentials --help` or `coreapi headers --help`. ## Codecs By default the command line client only includes support for reading Core JSON -schemas, however it for installing additional codecs. +schemas, however it includes a plugin system for installing additional codecs. $ pip install openapi-codec jsonhyperschema-codec hal-codec $ coreapi codecs show Codecs - corejson "application/vnd.coreapi+json" - openapi "application/openapi+json" - jsonhyperschema "application/schema+json" - hal "application/hal+json" + corejson application/vnd.coreapi+json encoding, decoding + hal application/hal+json encoding, decoding + openapi application/openapi+json encoding, decoding + jsonhyperschema application/schema+json decoding + json application/json data + text text/* data ## Utilities @@ -202,6 +204,10 @@ To load a schema file from disk: $ coreapi load my-api-schema.json --format corejson +To dump the current document to console in a given format: + + $ coreapi dump --format openapi + To remove the current document, along with all currently saved history, credentials, headers and bookmarks: From a9218e460f9569a630e0f86f38349cb3e73ca626 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 14 Jul 2016 12:44:13 +0100 Subject: [PATCH 019/175] Minor tutorial updates --- .../5-relationships-and-hyperlinked-apis.md | 6 +++--- docs/tutorial/7-schemas-and-client-libraries.md | 13 +++++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/tutorial/5-relationships-and-hyperlinked-apis.md b/docs/tutorial/5-relationships-and-hyperlinked-apis.md index 4b9347bfa..8cda09c62 100644 --- a/docs/tutorial/5-relationships-and-hyperlinked-apis.md +++ b/docs/tutorial/5-relationships-and-hyperlinked-apis.md @@ -18,7 +18,7 @@ Right now we have endpoints for 'snippets' and 'users', but we don't have a sing 'snippets': reverse('snippet-list', request=request, format=format) }) -Two things should be noticed here. First, we're using REST framework's `reverse` function in order to return fully-qualified URLs; second, URL patterns are identified by convenience names that we will declare later on in our `snippets/urls.py`. +Two things should be noticed here. First, we're using REST framework's `reverse` function in order to return fully-qualified URLs; second, URL patterns are identified by convenience names that we will declare later on in our `snippets/urls.py`. ## Creating an endpoint for the highlighted snippets @@ -80,7 +80,7 @@ We can easily re-write our existing serializers to use hyperlinking. In your `sn class Meta: model = Snippet - fields = ('url', 'highlight', 'owner', + fields = ('url', 'pk', 'highlight', 'owner', 'title', 'code', 'linenos', 'language', 'style') @@ -89,7 +89,7 @@ We can easily re-write our existing serializers to use hyperlinking. In your `sn class Meta: model = User - fields = ('url', 'username', 'snippets') + fields = ('url', 'pk', 'username', 'snippets') Notice that we've also added a new `'highlight'` field. This field is of the same type as the `url` field, except that it points to the `'snippet-highlight'` url pattern, instead of the `'snippet-detail'` url pattern. diff --git a/docs/tutorial/7-schemas-and-client-libraries.md b/docs/tutorial/7-schemas-and-client-libraries.md index 8d772a5bf..c2141489c 100644 --- a/docs/tutorial/7-schemas-and-client-libraries.md +++ b/docs/tutorial/7-schemas-and-client-libraries.md @@ -67,9 +67,13 @@ also supported. Now that our API is exposing a schema endpoint, we can use a dynamic client library to interact with the API. To demonstrate this, let's use the -Core API command line client. We've already installed the `coreapi` package -using `pip`, so the client tool should already be installed. Check that it -is available on the command line... +Core API command line client. + +The command line client is available as the `coreapi-cli` package: + + $ pip install coreapi-cli + +Now check that it is available on the command line... $ coreapi Usage: coreapi [OPTIONS] COMMAND [ARGS]... @@ -108,6 +112,7 @@ Let's try listing the existing snippets, using the command line client: [ { "url": "http://127.0.0.1:8000/snippets/1/", + "pk": 1, "highlight": "http://127.0.0.1:8000/snippets/1/highlight/", "owner": "lucy", "title": "Example", @@ -166,7 +171,7 @@ snippet: $ coreapi action snippets create --param title "Example" --param code "print('hello, world')" { "url": "http://127.0.0.1:8000/snippets/7/", - "id": 7, + "pk": 7, "highlight": "http://127.0.0.1:8000/snippets/7/highlight/", "owner": "lucy", "title": "Example", From 70e4a43ae3a03b6cf67133c1bb61cf13d0b25a6c Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 14 Jul 2016 12:56:57 +0100 Subject: [PATCH 020/175] Update coreapi requirement --- requirements/requirements-optionals.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements-optionals.txt b/requirements/requirements-optionals.txt index 54c080491..20436e6b4 100644 --- a/requirements/requirements-optionals.txt +++ b/requirements/requirements-optionals.txt @@ -2,4 +2,4 @@ markdown==2.6.4 django-guardian==1.4.3 django-filter==0.13.0 -coreapi==1.21.1 +coreapi==1.32.0 From faf6f226cd001648fc421eea62e7cadcc14597f1 Mon Sep 17 00:00:00 2001 From: anoopmalav Date: Thu, 14 Jul 2016 22:57:38 +0530 Subject: [PATCH 021/175] Fix Typo in index.md Currently generating invalid URL at index page. http://www.django-rest-framework.org/'api-guide/schemas.md' Though it is correct in navigation. --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index f64056cd8..87e013b8e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -346,7 +346,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [versioning]: api-guide/versioning.md [contentnegotiation]: api-guide/content-negotiation.md [metadata]: api-guide/metadata.md -[schemas]: 'api-guide/schemas.md' +[schemas]: api-guide/schemas.md [formatsuffixes]: api-guide/format-suffixes.md [reverse]: api-guide/reverse.md [exceptions]: api-guide/exceptions.md From 1a65f72f71e7b4d0e776695fe11800e499af3d44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Larra=C3=ADn?= Date: Fri, 15 Jul 2016 14:34:27 -0400 Subject: [PATCH 022/175] docs.settings: fix name of `VERSION_PARAM` --- docs/api-guide/settings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index a73c6c16d..ea018053f 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -181,7 +181,7 @@ If set, this value will restrict the set of versions that may be returned by the Default: `None` -#### VERSION_PARAMETER +#### VERSION_PARAM The string that should used for any versioning parameters, such as in the media type or URL query parameters. From a436515196de60ea75ffa39b14a4a82551b79428 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sat, 16 Jul 2016 22:44:49 +0200 Subject: [PATCH 023/175] Add missing return statement. Fix #4272. --- rest_framework/compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 9c69eaa03..94f64265a 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -125,7 +125,7 @@ def get_related_model(field): def value_from_object(field, obj): if django.VERSION < (1, 9): return field._get_val_from_obj(obj) - field.value_from_object(obj) + return field.value_from_object(obj) # contrib.postgres only supported from 1.8 onwards. From bea243a0cadc36b481a36931f29e4c321f54a36b Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Mon, 18 Jul 2016 16:56:36 +0200 Subject: [PATCH 024/175] Fix coreapi param arguments. (#4274) --- docs/tutorial/7-schemas-and-client-libraries.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/tutorial/7-schemas-and-client-libraries.md b/docs/tutorial/7-schemas-and-client-libraries.md index c2141489c..77cfdd3b3 100644 --- a/docs/tutorial/7-schemas-and-client-libraries.md +++ b/docs/tutorial/7-schemas-and-client-libraries.md @@ -126,7 +126,7 @@ Let's try listing the existing snippets, using the command line client: Some of the API endpoints require named parameters. For example, to get back the highlight HTML for a particular snippet we need to provide an id. - $ coreapi action snippets highlight --param pk 1 + $ coreapi action snippets highlight --param pk=1 @@ -168,7 +168,7 @@ set of available interactions. We're now able to interact with these endpoints. For example, to create a new snippet: - $ coreapi action snippets create --param title "Example" --param code "print('hello, world')" + $ coreapi action snippets create --param title="Example" --param code="print('hello, world')" { "url": "http://127.0.0.1:8000/snippets/7/", "pk": 7, @@ -183,7 +183,7 @@ snippet: And to delete a snippet: - $ coreapi action snippets destroy --param pk 7 + $ coreapi action snippets destroy --param pk=7 As well as the command line client, developers can also interact with your API using client libraries. The Python client library is the first of these From d80b0eaead9c36c86577f8a0d759e34204b8ea37 Mon Sep 17 00:00:00 2001 From: Marc Gibbons Date: Mon, 18 Jul 2016 11:04:23 -0400 Subject: [PATCH 025/175] Update schema generator example (#4267) 1. The returns from the views needs to be a Response 2. I found that instantiating the generator at the module level caused an import error when starting Django, likely because it is executing before all the app load magic happened. Moving it into the view method solved this. --- docs/api-guide/schemas.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index b39c35fa0..9d4dce93f 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -157,14 +157,14 @@ return the schema. **views.py:** from rest_framework.decorators import api_view, renderer_classes - from rest_framework import renderers, schemas + from rest_framework import renderers, response, schemas - generator = schemas.SchemaGenerator(title='Bookings API') @api_view() @renderer_classes([renderers.CoreJSONRenderer]) def schema_view(request): - return generator.get_schema() + generator = schemas.SchemaGenerator(title='Bookings API') + return response.Response(generator.get_schema()) **urls.py:** @@ -185,7 +185,8 @@ you need to pass the `request` argument to the `get_schema()` method, like so: @api_view() @renderer_classes([renderers.CoreJSONRenderer]) def schema_view(request): - return generator.get_schema(request=request) + generator = schemas.SchemaGenerator(title='Bookings API') + return response.Response(generator.get_schema(request=request)) ## Explicit schema definition @@ -196,7 +197,7 @@ representation. import coreapi from rest_framework.decorators import api_view, renderer_classes - from rest_framework import renderers + from rest_framework import renderers, response schema = coreapi.Document( title='Bookings API', @@ -208,7 +209,7 @@ representation. @api_view() @renderer_classes([renderers.CoreJSONRenderer]) def schema_view(request): - return schema + return response.Response(schema) ## Static schema file @@ -273,7 +274,8 @@ Returns a `coreapi.Document` instance that represents the API schema. @api_view @renderer_classes([renderers.CoreJSONRenderer]) def schema_view(request): - return generator.get_schema() + generator = schemas.SchemaGenerator(title='Bookings API') + return Response(generator.get_schema()) Arguments: From e476c222f98c4a455bf806fa05c5d70a9e0f37da Mon Sep 17 00:00:00 2001 From: Philippe Ombredanne Date: Mon, 18 Jul 2016 17:08:44 +0200 Subject: [PATCH 026/175] Add LICENSE.md to the built wheel (#4270) The wheel released on Pypi does not include the license file . The way to do this is by updating the setup.cfg accordingly --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.cfg b/setup.cfg index 5e4090017..fd8b0682b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,5 @@ [wheel] universal = 1 + +[metadata] +license_file = LICENSE.md From 71cdda93993bb326b13b724b1659f20c81350cd5 Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Tue, 19 Jul 2016 11:31:29 +0600 Subject: [PATCH 027/175] updated minor django versions --- tox.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index 1a160a299..89655aee2 100644 --- a/tox.ini +++ b/tox.ini @@ -15,9 +15,9 @@ setenv = PYTHONDONTWRITEBYTECODE=1 PYTHONWARNINGS=once deps = - django18: Django==1.8.13 - django19: Django==1.9.7 - django110: Django==1.10b1 + django18: Django==1.8.14 + django19: Django==1.9.8 + django110: Django==1.10rc1 djangomaster: https://github.com/django/django/archive/master.tar.gz -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt From 02a81d33621057225d0f3d7b9e3368793f428f28 Mon Sep 17 00:00:00 2001 From: Osvaldo Santana Neto Date: Tue, 19 Jul 2016 14:15:49 -0300 Subject: [PATCH 028/175] Fix SwaggerRenderer implementation example --- docs/api-guide/schemas.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 9d4dce93f..6585801e7 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -246,7 +246,7 @@ to the Open API ("Swagger") format: def render(self, data, media_type=None, renderer_context=None): codec = OpenAPICodec() - return OpenAPICodec.dump(data) + return codec.dump(data) --- From 48465a667a5583a2fcd8589793947af7130e3805 Mon Sep 17 00:00:00 2001 From: Hendra Date: Tue, 26 Jul 2016 18:03:05 +0800 Subject: [PATCH 029/175] Remove outdated resources (#4295) Removed links to Youtube videos that are no longer available and updated a link to a tutorial that has moved. --- docs/topics/third-party-resources.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/topics/third-party-resources.md b/docs/topics/third-party-resources.md index 4be88d618..4366269bb 100644 --- a/docs/topics/third-party-resources.md +++ b/docs/topics/third-party-resources.md @@ -273,8 +273,6 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque * [Ember and Django Part 1 (Video)][ember-and-django-part 1-video] * [Django Rest Framework Part 1 (Video)][django-rest-framework-part-1-video] -* [Pyowa July 2013 - Django Rest Framework (Video)][pyowa-july-2013-django-rest-framework-video] -* [django-rest-framework and angularjs (Video)][django-rest-framework-and-angularjs-video] ### Articles @@ -335,7 +333,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque [ember-django-adapter]: https://github.com/dustinfarris/ember-django-adapter [beginners-guide-to-the-django-rest-framework]: http://code.tutsplus.com/tutorials/beginners-guide-to-the-django-rest-framework--cms-19786 [getting-started-with-django-rest-framework-and-angularjs]: http://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html -[end-to-end-web-app-with-django-rest-framework-angularjs]: http://blog.mourafiq.com/post/55034504632/end-to-end-web-app-with-django-rest-framework +[end-to-end-web-app-with-django-rest-framework-angularjs]: http://mourafiq.com/2013/07/01/end-to-end-web-app-with-django-angular-1.html [start-your-api-django-rest-framework-part-1]: https://godjango.com/41-start-your-api-django-rest-framework-part-1/ [permissions-authentication-django-rest-framework-part-2]: https://godjango.com/43-permissions-authentication-django-rest-framework-part-2/ [viewsets-and-routers-django-rest-framework-part-3]: https://godjango.com/45-viewsets-and-routers-django-rest-framework-part-3/ @@ -343,8 +341,6 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque [check-credentials-using-django-rest-framework]: http://richardtier.com/2014/03/06/110/ [ember-and-django-part 1-video]: http://www.neckbeardrepublic.com/screencasts/ember-and-django-part-1 [django-rest-framework-part-1-video]: http://www.neckbeardrepublic.com/screencasts/django-rest-framework-part-1 -[pyowa-july-2013-django-rest-framework-video]: http://www.youtube.com/watch?v=e1zrehvxpbo -[django-rest-framework-and-angularjs-video]: http://www.youtube.com/watch?v=q8frbgtj020 [web-api-performance-profiling-django-rest-framework]: http://dabapps.com/blog/api-performance-profiling-django-rest-framework/ [api-development-with-django-and-django-rest-framework]: https://bnotions.com/api-development-with-django-and-django-rest-framework/ [django-rest-auth]: https://github.com/Tivix/django-rest-auth/ From 3ca0b15b17182f046555da9348f5c58796b54851 Mon Sep 17 00:00:00 2001 From: Andi Albrecht Date: Tue, 26 Jul 2016 12:07:03 +0200 Subject: [PATCH 030/175] Restore meta information for each search field. (#4298) The meta information stored in opts needs to be restored for each search field. Otherwise it references the wrong model when an attribute of a related model comes before an attribute of the original model in search fields. This doesn't apply to m2m relations since must_call_distinct returns True in that case. --- rest_framework/filters.py | 2 +- tests/test_filters.py | 41 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index caff1c17f..aaf313491 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -174,8 +174,8 @@ class SearchFilter(BaseFilterBackend): """ Return True if 'distinct()' should be used to query the given lookups. """ - opts = queryset.model._meta for search_field in search_fields: + opts = queryset.model._meta if search_field[0] in self.lookup_prefixes: search_field = search_field[1:] parts = search_field.split(LOOKUP_SEP) diff --git a/tests/test_filters.py b/tests/test_filters.py index 8bffd215c..175ae5b12 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -454,6 +454,47 @@ class AttributeModel(models.Model): label = models.CharField(max_length=32) +class SearchFilterModelFk(models.Model): + title = models.CharField(max_length=20) + attribute = models.ForeignKey(AttributeModel) + + +class SearchFilterFkSerializer(serializers.ModelSerializer): + class Meta: + model = SearchFilterModelFk + fields = '__all__' + + +class SearchFilterFkTests(TestCase): + + def test_must_call_distinct(self): + filter_ = filters.SearchFilter() + prefixes = [''] + list(filter_.lookup_prefixes) + for prefix in prefixes: + self.assertFalse( + filter_.must_call_distinct( + SearchFilterModelFk._meta, ["%stitle" % prefix] + ) + ) + self.assertFalse( + filter_.must_call_distinct( + SearchFilterModelFk._meta, ["%stitle" % prefix, "%sattribute__label" % prefix] + ) + ) + + def test_must_call_distinct_restores_meta_for_each_field(self): + # In this test case the attribute of the fk model comes first in the + # list of search fields. + filter_ = filters.SearchFilter() + prefixes = [''] + list(filter_.lookup_prefixes) + for prefix in prefixes: + self.assertFalse( + filter_.must_call_distinct( + SearchFilterModelFk._meta, ["%sattribute__label" % prefix, "%stitle" % prefix] + ) + ) + + class SearchFilterModelM2M(models.Model): title = models.CharField(max_length=20) text = models.CharField(max_length=100) From da2ff1bc4a6865c8f061e2078b015de28bbcecce Mon Sep 17 00:00:00 2001 From: Gustavo Pantuza Date: Tue, 26 Jul 2016 07:09:12 -0300 Subject: [PATCH 031/175] Adding support for RFC 4918 status codes (#4291) * Added HTTP status codes from RFC 4918 * Updated documentation about status codes * Added reference to RFC 4918 --- docs/api-guide/status-codes.md | 5 +++++ rest_framework/status.py | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/docs/api-guide/status-codes.md b/docs/api-guide/status-codes.md index 398c04804..f6ec3598f 100644 --- a/docs/api-guide/status-codes.md +++ b/docs/api-guide/status-codes.md @@ -50,6 +50,7 @@ This class of status code indicates that the client's request was successfully r HTTP_204_NO_CONTENT HTTP_205_RESET_CONTENT HTTP_206_PARTIAL_CONTENT + HTTP_207_MULTI_STATUS ## Redirection - 3xx @@ -86,6 +87,9 @@ The 4xx class of status code is intended for cases in which the client seems to HTTP_415_UNSUPPORTED_MEDIA_TYPE HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE HTTP_417_EXPECTATION_FAILED + HTTP_422_UNPROCESSABLE_ENTITY + HTTP_423_LOCKED + HTTP_424_FAILED_DEPENDENCY HTTP_428_PRECONDITION_REQUIRED HTTP_429_TOO_MANY_REQUESTS HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE @@ -101,6 +105,7 @@ Response status codes beginning with the digit "5" indicate cases in which the s HTTP_503_SERVICE_UNAVAILABLE HTTP_504_GATEWAY_TIMEOUT HTTP_505_HTTP_VERSION_NOT_SUPPORTED + HTTP_507_INSUFFICIENT_STORAGE HTTP_511_NETWORK_AUTHENTICATION_REQUIRED ## Helper functions diff --git a/rest_framework/status.py b/rest_framework/status.py index ed1b4784b..c016b63c6 100644 --- a/rest_framework/status.py +++ b/rest_framework/status.py @@ -3,6 +3,7 @@ Descriptive HTTP status codes, for code readability. See RFC 2616 - http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html And RFC 6585 - http://tools.ietf.org/html/rfc6585 +And RFC 4918 - https://tools.ietf.org/html/rfc4918 """ from __future__ import unicode_literals @@ -36,6 +37,7 @@ HTTP_203_NON_AUTHORITATIVE_INFORMATION = 203 HTTP_204_NO_CONTENT = 204 HTTP_205_RESET_CONTENT = 205 HTTP_206_PARTIAL_CONTENT = 206 +HTTP_207_MULTI_STATUS = 207 HTTP_300_MULTIPLE_CHOICES = 300 HTTP_301_MOVED_PERMANENTLY = 301 HTTP_302_FOUND = 302 @@ -62,6 +64,9 @@ HTTP_414_REQUEST_URI_TOO_LONG = 414 HTTP_415_UNSUPPORTED_MEDIA_TYPE = 415 HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE = 416 HTTP_417_EXPECTATION_FAILED = 417 +HTTP_422_UNPROCESSABLE_ENTITY = 422 +HTTP_423_LOCKED = 423 +HTTP_424_FAILED_DEPENDENCY = 424 HTTP_428_PRECONDITION_REQUIRED = 428 HTTP_429_TOO_MANY_REQUESTS = 429 HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE = 431 @@ -72,4 +77,5 @@ HTTP_502_BAD_GATEWAY = 502 HTTP_503_SERVICE_UNAVAILABLE = 503 HTTP_504_GATEWAY_TIMEOUT = 504 HTTP_505_HTTP_VERSION_NOT_SUPPORTED = 505 +HTTP_507_INSUFFICIENT_STORAGE = 507 HTTP_511_NETWORK_AUTHENTICATION_REQUIRED = 511 From b63099084f471fe3cd477fc5a5aa90cc97fca827 Mon Sep 17 00:00:00 2001 From: Asif Saifuddin Auvi Date: Tue, 26 Jul 2016 19:09:04 +0600 Subject: [PATCH 032/175] twitter bootstrap minor update (#4307) * updated bootstrap min css to 3.3.7 * updated bootstrap min javascript to 3.3.7 --- .../static/rest_framework/css/bootstrap.min.css | 7 ++++--- rest_framework/static/rest_framework/js/bootstrap.min.js | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/rest_framework/static/rest_framework/css/bootstrap.min.css b/rest_framework/static/rest_framework/css/bootstrap.min.css index d65c66b1b..ed3905e0e 100644 --- a/rest_framework/static/rest_framework/css/bootstrap.min.css +++ b/rest_framework/static/rest_framework/css/bootstrap.min.css @@ -1,5 +1,6 @@ /*! - * Bootstrap v3.3.5 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:3;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} \ No newline at end of file + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/rest_framework/static/rest_framework/js/bootstrap.min.js b/rest_framework/static/rest_framework/js/bootstrap.min.js index 133aeecb9..9bcd2fcca 100644 --- a/rest_framework/static/rest_framework/js/bootstrap.min.js +++ b/rest_framework/static/rest_framework/js/bootstrap.min.js @@ -1,7 +1,7 @@ /*! - * Bootstrap v3.3.5 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. * Licensed under the MIT license */ -if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.5",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.5",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.5",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.5",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth

',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.5",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.5",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.5",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(a>this.$items.length-1||a<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.7",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.7",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);if(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),!c.isInStateTrue())return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null,a.$element=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.7",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.7",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file From f0f61aa077ba49f3f107ec012f021cb8fa27ac07 Mon Sep 17 00:00:00 2001 From: Sassan Haradji Date: Tue, 26 Jul 2016 18:42:51 +0430 Subject: [PATCH 033/175] use verbose_name instead of object_name in field_mapping (#4299) * use verbose_name instead of object_name in error messages --- rest_framework/utils/field_mapping.py | 2 +- tests/test_validators.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index af7ab0231..311d58317 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -220,7 +220,7 @@ def get_field_kwargs(field_name, model_field): unique_error_message = model_field.error_messages.get('unique', None) if unique_error_message: unique_error_message = unique_error_message % { - 'model_name': model_field.model._meta.object_name, + 'model_name': model_field.model._meta.verbose_name, 'field_label': model_field.verbose_name } validator = UniqueValidator( diff --git a/tests/test_validators.py b/tests/test_validators.py index 32d41f2ba..ebc8b899f 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -77,7 +77,7 @@ class TestUniquenessValidation(TestCase): data = {'username': 'existing'} serializer = UniquenessSerializer(data=data) assert not serializer.is_valid() - assert serializer.errors == {'username': ['UniquenessModel with this username already exists.']} + assert serializer.errors == {'username': ['uniqueness model with this username already exists.']} def test_is_unique(self): data = {'username': 'other'} From 5d3b56f957d5ba6d5cb6e680c853a2e0da026fbb Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 26 Jul 2016 16:28:10 +0100 Subject: [PATCH 034/175] Test case for #4272 (#4310) * Test case for #4272 --- tests/test_model_serializer.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index f24d1d515..2cf6cb04c 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -955,3 +955,24 @@ class TestMetaInheritance(TestCase): self.assertEqual(unicode_repr(ChildSerializer()), child_expected) self.assertEqual(unicode_repr(TestSerializer()), test_expected) self.assertEqual(unicode_repr(ChildSerializer()), child_expected) + + +class OneToOneTargetTestModel(models.Model): + text = models.CharField(max_length=100) + + +class OneToOneSourceTestModel(models.Model): + target = models.OneToOneField(OneToOneTargetTestModel, primary_key=True) + + +class TestModelFieldValues(TestCase): + def test_model_field(self): + class ExampleSerializer(serializers.ModelSerializer): + class Meta: + model = OneToOneSourceTestModel + fields = ('target',) + + target = OneToOneTargetTestModel(id=1, text='abc') + source = OneToOneSourceTestModel(target=target) + serializer = ExampleSerializer(source) + self.assertEqual(serializer.data, {'target': 1}) From 19b415ec25089d16d7b78c99712522adf6a5ea02 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 26 Jul 2016 16:45:27 +0100 Subject: [PATCH 035/175] Improve pagination docs. Refs #4304 [ci skip] --- docs/api-guide/pagination.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 0dd935ba3..088e07184 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -21,12 +21,15 @@ Pagination can be turned off by setting the pagination class to `None`. ## Setting the pagination style -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: +The default pagination style may be set globally, using the `DEFAULT_PAGINATION_CLASS` and `PAGE_SIZE` setting keys. For example, to use the built-in limit/offset pagination, you would do something like this: REST_FRAMEWORK = { - 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination' + 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', + 'PAGE_SIZE': 100 } +Note that you need to set both the pagination class, and the page size that should be used. + 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. ## Modifying the pagination style @@ -109,7 +112,7 @@ To set these attributes you should override the `PageNumberPagination` class, an ## LimitOffsetPagination -This pagination style mirrors the syntax used when looking up multiple database records. The client includes both a "limit" and an +This pagination style mirrors the syntax used when looking up multiple database records. The client includes both a "limit" and an "offset" query parameter. The limit indicates the maximum number of items to return, and is equivalent to the `page_size` in other styles. The offset indicates the starting position of the query in relation to the complete set of unpaginated items. **Request**: From 351e0a4a99a6505b01718aa8926b6e466e43dcb0 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 27 Jul 2016 11:49:01 +0100 Subject: [PATCH 036/175] Fix json indent parameter. Closes #4281 (#4313) --- docs/api-guide/schemas.md | 2 +- rest_framework/response.py | 4 +++- rest_framework/routers.py | 2 ++ tests/test_negotiation.py | 13 ++++++++++++- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 6585801e7..8abd74646 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -241,7 +241,7 @@ to the Open API ("Swagger") format: from openapi_codec import OpenAPICodec class SwaggerRenderer(renderers.BaseRenderer): - media_type = 'application/openapi+json;version=2.0' + media_type = 'application/openapi+json' format = 'swagger' def render(self, data, media_type=None, renderer_context=None): diff --git a/rest_framework/response.py b/rest_framework/response.py index ab8aff114..4b863cb99 100644 --- a/rest_framework/response.py +++ b/rest_framework/response.py @@ -51,9 +51,11 @@ class Response(SimpleTemplateResponse): @property def rendered_content(self): renderer = getattr(self, 'accepted_renderer', None) + accepted_media_type = getattr(self, 'accepted_media_type', None) context = getattr(self, 'renderer_context', None) assert renderer, ".accepted_renderer not set on Response" + assert accepted_media_type, ".accepted_media_type not set on Response" assert context, ".renderer_context not set on Response" context['response'] = self @@ -67,7 +69,7 @@ class Response(SimpleTemplateResponse): content_type = media_type self['Content-Type'] = content_type - ret = renderer.render(self.data, media_type, context) + ret = renderer.render(self.data, accepted_media_type, context) if isinstance(ret, six.text_type): assert charset, ( 'renderer returned unicode, and did not specify ' diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 69d73e842..9561fa4db 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -276,6 +276,8 @@ class DefaultRouter(SimpleRouter): default_schema_renderers = [renderers.CoreJSONRenderer] def __init__(self, *args, **kwargs): + if 'schema_renderers' in kwargs: + assert 'schema_title' in kwargs, 'Missing "schema_title" argument.' self.schema_title = kwargs.pop('schema_title', None) self.schema_renderers = kwargs.pop('schema_renderers', self.default_schema_renderers) super(DefaultRouter, self).__init__(*args, **kwargs) diff --git a/tests/test_negotiation.py b/tests/test_negotiation.py index 434fba496..96ef83eab 100644 --- a/tests/test_negotiation.py +++ b/tests/test_negotiation.py @@ -10,6 +10,11 @@ from rest_framework.test import APIRequestFactory factory = APIRequestFactory() +class MockOpenAPIRenderer(BaseRenderer): + media_type = 'application/openapi+json;version=2.0' + format = 'swagger' + + class MockJSONRenderer(BaseRenderer): media_type = 'application/json' @@ -24,7 +29,7 @@ class NoCharsetSpecifiedRenderer(BaseRenderer): class TestAcceptedMediaType(TestCase): def setUp(self): - self.renderers = [MockJSONRenderer(), MockHTMLRenderer()] + self.renderers = [MockJSONRenderer(), MockHTMLRenderer(), MockOpenAPIRenderer()] self.negotiator = DefaultContentNegotiation() def select_renderer(self, request): @@ -44,3 +49,9 @@ class TestAcceptedMediaType(TestCase): request = Request(factory.get('/', HTTP_ACCEPT='application/json; indent=8')) accepted_renderer, accepted_media_type = self.select_renderer(request) self.assertEqual(accepted_media_type, 'application/json; indent=8') + + def test_client_specifies_parameter(self): + request = Request(factory.get('/', HTTP_ACCEPT='application/openapi+json;version=2.0')) + accepted_renderer, accepted_media_type = self.select_renderer(request) + self.assertEqual(accepted_media_type, 'application/openapi+json;version=2.0') + self.assertEqual(accepted_renderer.format, 'swagger') From 8ebf81b1502bee067fe842fa23442aa4bc176473 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 27 Jul 2016 13:02:48 +0100 Subject: [PATCH 037/175] Schema support should function when 'pagination_class = None' (#4314) --- rest_framework/schemas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py index cf84aca74..523b2f1ec 100644 --- a/rest_framework/schemas.py +++ b/rest_framework/schemas.py @@ -278,7 +278,7 @@ class SchemaGenerator(object): if hasattr(callback, 'actions') and ('list' not in callback.actions.values()): return [] - if not hasattr(view, 'pagination_class'): + if not getattr(view, 'pagination_class', None): return [] paginator = view.pagination_class() From 3586c8a61ae0dd3424c49b50f22a7699e999bae7 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 27 Jul 2016 14:43:45 +0100 Subject: [PATCH 038/175] Set view.format_kwarg in schema generator (#4315) --- rest_framework/schemas.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py index 523b2f1ec..91fdc76a0 100644 --- a/rest_framework/schemas.py +++ b/rest_framework/schemas.py @@ -84,6 +84,7 @@ class SchemaGenerator(object): method = link.action.upper() view = callback.cls() view.request = clone_request(request, method) + view.format_kwarg = None try: view.check_permissions(view.request) except exceptions.APIException: From 46a870c002ff7b079fd70a7730b666d16b2ba9e1 Mon Sep 17 00:00:00 2001 From: Alexander Gaevsky Date: Wed, 27 Jul 2016 17:36:36 +0300 Subject: [PATCH 039/175] Fix schema generation for APIView, since it does not have get_serializer_class method. (#4285) --- rest_framework/schemas.py | 4 +++- tests/test_schemas.py | 42 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py index 91fdc76a0..cdf2d5f3e 100644 --- a/rest_framework/schemas.py +++ b/rest_framework/schemas.py @@ -112,7 +112,6 @@ class SchemaGenerator(object): for pattern in patterns: path_regex = prefix + pattern.regex.pattern - if isinstance(pattern, RegexURLPattern): path = self.get_path(path_regex) callback = pattern.callback @@ -254,6 +253,9 @@ class SchemaGenerator(object): fields = [] + if not (hasattr(view, 'get_serializer_class') and callable(getattr(view, 'get_serializer_class'))): + return [] + serializer_class = view.get_serializer_class() serializer = serializer_class() diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 7d3308ed9..a32b8a117 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -5,8 +5,11 @@ from django.test import TestCase, override_settings from rest_framework import filters, pagination, permissions, serializers from rest_framework.compat import coreapi +from rest_framework.response import Response from rest_framework.routers import DefaultRouter +from rest_framework.schemas import SchemaGenerator from rest_framework.test import APIClient +from rest_framework.views import APIView from rest_framework.viewsets import ModelViewSet @@ -31,11 +34,24 @@ class ExampleViewSet(ModelViewSet): serializer_class = ExampleSerializer +class ExampleView(APIView): + permission_classes = [permissions.IsAuthenticatedOrReadOnly] + + def get(self, request, *args, **kwargs): + return Response() + + def post(self, request, *args, **kwargs): + return Response() + + router = DefaultRouter(schema_title='Example API' if coreapi else None) router.register('example', ExampleViewSet, base_name='example') urlpatterns = [ url(r'^', include(router.urls)) ] +urlpatterns2 = [ + url(r'^example-view/$', ExampleView.as_view(), name='example-view') +] @unittest.skipUnless(coreapi, 'coreapi is not installed') @@ -135,3 +151,29 @@ class TestRouterGeneratedSchema(TestCase): } ) self.assertEqual(response.data, expected) + + +@unittest.skipUnless(coreapi, 'coreapi is not installed') +class TestSchemaGenerator(TestCase): + def test_view(self): + schema_generator = SchemaGenerator(title='Test View', patterns=urlpatterns2) + schema = schema_generator.get_schema() + expected = coreapi.Document( + url='', + title='Test View', + content={ + 'example-view': { + 'create': coreapi.Link( + url='/example-view/', + action='post', + fields=[] + ), + 'read': coreapi.Link( + url='/example-view/', + action='get', + fields=[] + ) + } + } + ) + self.assertEquals(schema, expected) From 1acbc29d580e7f36552c838d83ec3b80419d51da Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 27 Jul 2016 15:39:46 +0100 Subject: [PATCH 040/175] Minor style tweak. [ci skip] --- rest_framework/schemas.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py index cdf2d5f3e..ee99a1c45 100644 --- a/rest_framework/schemas.py +++ b/rest_framework/schemas.py @@ -251,11 +251,11 @@ class SchemaGenerator(object): if method not in ('PUT', 'PATCH', 'POST'): return [] - fields = [] - - if not (hasattr(view, 'get_serializer_class') and callable(getattr(view, 'get_serializer_class'))): + if not hasattr(view, 'get_serializer_class'): return [] + fields = [] + serializer_class = view.get_serializer_class() serializer = serializer_class() From 6a7d34ec3452d4e8534f5c9bd238405548485a7a Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 27 Jul 2016 15:40:04 +0100 Subject: [PATCH 041/175] Unique together checks should apply to fields that are read only, but have a default. (#4316) --- rest_framework/serializers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 8c39202f4..d5e6b66ed 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -1396,9 +1396,8 @@ class ModelSerializer(Serializer): # cannot map to a field, and must be a traversal, so we're not # including those. field_names = { - field.source for field in self.fields.values() + field.source for field in self._writable_fields if (field.source != '*') and ('.' not in field.source) - and not field.read_only } # Note that we make sure to check `unique_together` both on the From 061e0ed0841274da930f61838c60af1324ea04bc Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 28 Jul 2016 12:08:34 +0100 Subject: [PATCH 042/175] Added url and schema_url arguments (#4321) --- docs/api-guide/schemas.md | 20 +++++++++++++++++--- rest_framework/compat.py | 6 ++++++ rest_framework/routers.py | 12 ++++++++---- rest_framework/schemas.py | 12 ++++++++---- 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 8abd74646..848e57420 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -128,9 +128,11 @@ that include the Core JSON media type in their `Accept` header. This is a great zero-configuration option for when you want to get up and running really quickly. -The only other available option to `DefaultRouter` is `schema_renderers`, which -may be used to pass the set of renderer classes that can be used to render -schema output. +The other available options to `DefaultRouter` are: + +#### schema_renderers + +May be used to pass the set of renderer classes that can be used to render schema output. from rest_framework.renderers import CoreJSONRenderer from my_custom_package import APIBlueprintRenderer @@ -139,6 +141,17 @@ schema output. CoreJSONRenderer, APIBlueprintRenderer ]) +#### schema_url + +May be used to pass the root URL for the schema. This can either be used to ensure that +the schema URLs include a canonical hostname and schema, or to ensure that all the +schema URLs include a path prefix. + + router = DefaultRouter( + schema_title='Server Monitoring API', + schema_url='https://www.example.org/api/' + ) + If you want more flexibility over the schema output then you'll need to consider using `SchemaGenerator` instead. @@ -264,6 +277,7 @@ Typically you'll instantiate `SchemaGenerator` with a single argument, like so: Arguments: * `title` - The name of the API. **required** +* `url` - The root URL of the API schema. This option is not required unless the schema is included under path prefix. * `patterns` - A list of URLs to inspect when generating the schema. Defaults to the project's URL conf. * `urlconf` - A URL conf module name to use when generating the schema. Defaults to `settings.ROOT_URLCONF`. diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 94f64265a..3143e7654 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -23,6 +23,12 @@ except ImportError: from django.utils import importlib # Will be removed in Django 1.9 +try: + import urlparse # Python 2.x +except ImportError: + import urllib.parse as urlparse + + def unicode_repr(instance): # Get the repr of an instance, but ensure it is a unicode string # on both python 3 (already the case) and 2 (not the case). diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 9561fa4db..099c56d6c 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -278,11 +278,14 @@ class DefaultRouter(SimpleRouter): def __init__(self, *args, **kwargs): if 'schema_renderers' in kwargs: assert 'schema_title' in kwargs, 'Missing "schema_title" argument.' + if 'schema_url' in kwargs: + assert 'schema_title' in kwargs, 'Missing "schema_title" argument.' self.schema_title = kwargs.pop('schema_title', None) + self.schema_url = kwargs.pop('schema_url', None) self.schema_renderers = kwargs.pop('schema_renderers', self.default_schema_renderers) super(DefaultRouter, self).__init__(*args, **kwargs) - def get_api_root_view(self, schema_urls=None): + def get_api_root_view(self, api_urls=None): """ Return a view to use as the API root. """ @@ -294,11 +297,12 @@ class DefaultRouter(SimpleRouter): view_renderers = list(api_settings.DEFAULT_RENDERER_CLASSES) schema_media_types = [] - if schema_urls and self.schema_title: + if api_urls and self.schema_title: view_renderers += list(self.schema_renderers) schema_generator = SchemaGenerator( title=self.schema_title, - patterns=schema_urls + url=self.schema_url, + patterns=api_urls ) schema_media_types = [ renderer.media_type @@ -347,7 +351,7 @@ class DefaultRouter(SimpleRouter): urls = super(DefaultRouter, self).get_urls() if self.include_root_view: - view = self.get_api_root_view(schema_urls=urls) + view = self.get_api_root_view(api_urls=urls) root_url = url(r'^$', view, name=self.root_view_name) urls.append(root_url) diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py index ee99a1c45..41dc82da1 100644 --- a/rest_framework/schemas.py +++ b/rest_framework/schemas.py @@ -6,7 +6,7 @@ from django.core.urlresolvers import RegexURLPattern, RegexURLResolver from django.utils import six from rest_framework import exceptions, serializers -from rest_framework.compat import coreapi, uritemplate +from rest_framework.compat import coreapi, uritemplate, urlparse from rest_framework.request import clone_request from rest_framework.views import APIView @@ -57,7 +57,7 @@ class SchemaGenerator(object): 'delete': 'destroy', } - def __init__(self, title=None, patterns=None, urlconf=None): + def __init__(self, title=None, url=None, patterns=None, urlconf=None): assert coreapi, '`coreapi` must be installed for schema support.' if patterns is None and urlconf is not None: @@ -70,7 +70,11 @@ class SchemaGenerator(object): urls = import_module(settings.ROOT_URLCONF) patterns = urls.urlpatterns + if url and not url.endswith('/'): + url += '/' + self.title = title + self.url = url self.endpoints = self.get_api_endpoints(patterns) def get_schema(self, request=None): @@ -102,7 +106,7 @@ class SchemaGenerator(object): insert_into(content, key, link) # Return the schema document. - return coreapi.Document(title=self.title, content=content) + return coreapi.Document(title=self.title, content=content, url=self.url) def get_api_endpoints(self, patterns, prefix=''): """ @@ -203,7 +207,7 @@ class SchemaGenerator(object): encoding = None return coreapi.Link( - url=path, + url=urlparse.urljoin(self.url, path), action=method.lower(), encoding=encoding, fields=fields From 306726d9e8645aeb9528a3f6c6674d0fe5471374 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 28 Jul 2016 12:25:21 +0100 Subject: [PATCH 043/175] Improve datetime format docs (#4322) --- docs/api-guide/fields.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index f95608afb..a7ad4f70c 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -290,9 +290,9 @@ A date and time representation. Corresponds to `django.db.models.fields.DateTimeField`. -**Signature:** `DateTimeField(format=None, input_formats=None)` +**Signature:** `DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)` -* `format` - A string representing the output format. If not specified, this defaults to the same value as the `DATETIME_FORMAT` settings key, which will be `'iso-8601'` unless set. Setting to a format string indicates that `to_representation` return values should be coerced to string output. Format strings are described below. Setting this value to `None` indicates that Python `datetime` objects should be returned by `to_representation`. In this case the datetime encoding will be determined by the renderer. +* `format` - A string representing the output format. If not specified, this defaults to the same value as the `DATETIME_FORMAT` settings key, which will be `'iso-8601'` unless set. Setting to a format string indicates that `to_representation` return values should be coerced to string output. Format strings are described below. Setting this value to `None` indicates that Python `datetime` objects should be returned by `to_representation`. In this case the datetime encoding will be determined by the renderer. * `input_formats` - A list of strings representing the input formats which may be used to parse the date. If not specified, the `DATETIME_INPUT_FORMATS` setting will be used, which defaults to `['iso-8601']`. #### `DateTimeField` format strings. @@ -321,7 +321,7 @@ A date representation. Corresponds to `django.db.models.fields.DateField` -**Signature:** `DateField(format=None, input_formats=None)` +**Signature:** `DateField(format=api_settings.DATE_FORMAT, input_formats=None)` * `format` - A string representing the output format. If not specified, this defaults to the same value as the `DATE_FORMAT` settings key, which will be `'iso-8601'` unless set. Setting to a format string indicates that `to_representation` return values should be coerced to string output. Format strings are described below. Setting this value to `None` indicates that Python `date` objects should be returned by `to_representation`. In this case the date encoding will be determined by the renderer. * `input_formats` - A list of strings representing the input formats which may be used to parse the date. If not specified, the `DATE_INPUT_FORMATS` setting will be used, which defaults to `['iso-8601']`. @@ -336,7 +336,7 @@ A time representation. Corresponds to `django.db.models.fields.TimeField` -**Signature:** `TimeField(format=None, input_formats=None)` +**Signature:** `TimeField(format=api_settings.TIME_FORMAT, input_formats=None)` * `format` - A string representing the output format. If not specified, this defaults to the same value as the `TIME_FORMAT` settings key, which will be `'iso-8601'` unless set. Setting to a format string indicates that `to_representation` return values should be coerced to string output. Format strings are described below. Setting this value to `None` indicates that Python `time` objects should be returned by `to_representation`. In this case the time encoding will be determined by the renderer. * `input_formats` - A list of strings representing the input formats which may be used to parse the date. If not specified, the `TIME_INPUT_FORMATS` setting will be used, which defaults to `['iso-8601']`. From e407dc7f019120540f57dc9a37bcc53a99ff2d9d Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 28 Jul 2016 12:50:51 +0100 Subject: [PATCH 044/175] Added root_renderers argument (#4323) --- docs/api-guide/schemas.md | 4 ++++ rest_framework/routers.py | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 848e57420..7f8af723e 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -155,6 +155,10 @@ schema URLs include a path prefix. If you want more flexibility over the schema output then you'll need to consider using `SchemaGenerator` instead. +#### root_renderers + +May be used to pass the set of renderer classes that can be used to render the API root endpoint. + ## Using SchemaGenerator The most common way to add a schema to your API is to use the `SchemaGenerator` diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 099c56d6c..4eec70bda 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -283,6 +283,10 @@ class DefaultRouter(SimpleRouter): self.schema_title = kwargs.pop('schema_title', None) self.schema_url = kwargs.pop('schema_url', None) self.schema_renderers = kwargs.pop('schema_renderers', self.default_schema_renderers) + if 'root_renderers' in kwargs: + self.root_renderers = kwargs.pop('root_renderers') + else: + self.root_renderers = list(api_settings.DEFAULT_RENDERER_CLASSES) super(DefaultRouter, self).__init__(*args, **kwargs) def get_api_root_view(self, api_urls=None): @@ -294,7 +298,7 @@ class DefaultRouter(SimpleRouter): for prefix, viewset, basename in self.registry: api_root_dict[prefix] = list_name.format(basename=basename) - view_renderers = list(api_settings.DEFAULT_RENDERER_CLASSES) + view_renderers = list(self.root_renderers) schema_media_types = [] if api_urls and self.schema_title: From 449ec1d724987885caa295e97ff98a50a06c718c Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 28 Jul 2016 13:34:35 +0100 Subject: [PATCH 045/175] Version 3.4.1 [ci skip] (#4326) --- docs/topics/release-notes.md | 47 ++++++++++++++++++++++++++++++++++++ rest_framework/__init__.py | 2 +- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 5bfdad09b..831bf3446 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,6 +40,24 @@ You can determine your currently installed version using `pip freeze`: ## 3.4.x series + ### 3.4.1 + + **Date**: [28th July 2016][3.4.1-milestone] + +* Added `root_renderers` argument to `DefaultRouter`. ([#4323][gh4323], [#4268][gh4268]) +* Added `url` and `schema_url` arguments. ([#4321][gh4321], [#4308][gh4308], [#4305][gh4305]) +* Unique together checks should apply to read-only fields which have a default. ([#4316][gh4316], [#4294][gh4294]) +* Set view.format_kwarg in schema generator. ([#4293][gh4293], [#4315][gh4315]) +* Fix schema generator for views with `pagination_class = None`. ([#4314][gh4314], [#4289][gh4289]) +* Fix schema generator for views with no `get_serializer_class`. ([#4265][gh4265], [#4285][gh4285]) +* Fixes for media type parameters in `Accept` and `Content-Type` headers. ([#4287][gh4287], [#4313][gh4313], [#4281][gh4281]) +* Use verbose_name instead of object_name in error messages. ([#4299][gh4299]) +* Minor version update to Twitter Bootstrap. ([#4307][gh4307]) +* SearchFilter raises error when using with related field. ([#4302][gh4302], [#4303][gh4303], [#4298][gh4298]) +* Adding support for RFC 4918 status codes. ([#4291][gh4291]) +* Add LICENSE.md to the built wheel. ([#4270][gh4270]) +* Serializing "complex" field returns None instead of the value since 3.4 ([#4272][gh4272], [#4273][gh4273], [#4288][gh4288]) + ### 3.4.0 **Date**: [14th July 2016][3.4.0-milestone] @@ -495,6 +513,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.3.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.3.2+Release%22 [3.3.3-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.3.3+Release%22 [3.4.0-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.0+Release%22 +[3.4.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.1+Release%22 [gh2013]: https://github.com/tomchristie/django-rest-framework/issues/2013 @@ -896,3 +915,31 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh4255]: https://github.com/tomchristie/django-rest-framework/issues/4255 [gh4256]: https://github.com/tomchristie/django-rest-framework/issues/4256 [gh4259]: https://github.com/tomchristie/django-rest-framework/issues/4259 + + +[gh4323]: https://github.com/tomchristie/django-rest-framework/issues/4323 +[gh4268]: https://github.com/tomchristie/django-rest-framework/issues/4268 +[gh4321]: https://github.com/tomchristie/django-rest-framework/issues/4321 +[gh4308]: https://github.com/tomchristie/django-rest-framework/issues/4308 +[gh4305]: https://github.com/tomchristie/django-rest-framework/issues/4305 +[gh4316]: https://github.com/tomchristie/django-rest-framework/issues/4316 +[gh4294]: https://github.com/tomchristie/django-rest-framework/issues/4294 +[gh4293]: https://github.com/tomchristie/django-rest-framework/issues/4293 +[gh4315]: https://github.com/tomchristie/django-rest-framework/issues/4315 +[gh4314]: https://github.com/tomchristie/django-rest-framework/issues/4314 +[gh4289]: https://github.com/tomchristie/django-rest-framework/issues/4289 +[gh4265]: https://github.com/tomchristie/django-rest-framework/issues/4265 +[gh4285]: https://github.com/tomchristie/django-rest-framework/issues/4285 +[gh4287]: https://github.com/tomchristie/django-rest-framework/issues/4287 +[gh4313]: https://github.com/tomchristie/django-rest-framework/issues/4313 +[gh4281]: https://github.com/tomchristie/django-rest-framework/issues/4281 +[gh4299]: https://github.com/tomchristie/django-rest-framework/issues/4299 +[gh4307]: https://github.com/tomchristie/django-rest-framework/issues/4307 +[gh4302]: https://github.com/tomchristie/django-rest-framework/issues/4302 +[gh4303]: https://github.com/tomchristie/django-rest-framework/issues/4303 +[gh4298]: https://github.com/tomchristie/django-rest-framework/issues/4298 +[gh4291]: https://github.com/tomchristie/django-rest-framework/issues/4291 +[gh4270]: https://github.com/tomchristie/django-rest-framework/issues/4270 +[gh4272]: https://github.com/tomchristie/django-rest-framework/issues/4272 +[gh4273]: https://github.com/tomchristie/django-rest-framework/issues/4273 +[gh4288]: https://github.com/tomchristie/django-rest-framework/issues/4288 diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index ab1a16597..5c61f8d7f 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ ______ _____ _____ _____ __ """ __title__ = 'Django REST framework' -__version__ = '3.4.0' +__version__ = '3.4.1' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2016 Tom Christie' From 48a2f084aa2a616673c9adc5786f710acdc97d22 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 28 Jul 2016 13:38:05 +0100 Subject: [PATCH 046/175] Minor docs tweak [ci skip] --- docs/topics/release-notes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 831bf3446..025231ccf 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,9 +40,9 @@ You can determine your currently installed version using `pip freeze`: ## 3.4.x series - ### 3.4.1 +### 3.4.1 - **Date**: [28th July 2016][3.4.1-milestone] +**Date**: [28th July 2016][3.4.1-milestone] * Added `root_renderers` argument to `DefaultRouter`. ([#4323][gh4323], [#4268][gh4268]) * Added `url` and `schema_url` arguments. ([#4321][gh4321], [#4308][gh4308], [#4305][gh4305]) From 5b071ab35e9f3b1d3f06e90741ded0e10e8a1651 Mon Sep 17 00:00:00 2001 From: Jaap Roes Date: Mon, 1 Aug 2016 13:05:47 +0200 Subject: [PATCH 047/175] Remove note about Django 1.3 (#4334) Remove note about Django 1.3 --- docs/api-guide/filtering.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index 8664dcc8a..0ccd51dd3 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -241,7 +241,6 @@ For more details on using filter sets see the [django-filter documentation][djan * By default filtering is not enabled. If you want to use `DjangoFilterBackend` remember to make sure it is installed by using the `'DEFAULT_FILTER_BACKENDS'` setting. * When using boolean fields, you should use the values `True` and `False` in the URL query parameters, rather than `0`, `1`, `true` or `false`. (The allowed boolean values are currently hardwired in Django's [NullBooleanSelect implementation][nullbooleanselect].) * `django-filter` supports filtering across relationships, using Django's double-underscore syntax. -* For Django 1.3 support, make sure to install `django-filter` version 0.5.4, as later versions drop support for 1.3. --- From e997713313c36808ac7052a218b253087622e179 Mon Sep 17 00:00:00 2001 From: jsurloppe Date: Mon, 1 Aug 2016 15:14:55 +0200 Subject: [PATCH 048/175] urljoin with leading slash remove part of path (#4332) --- rest_framework/schemas.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py index 41dc82da1..02960083c 100644 --- a/rest_framework/schemas.py +++ b/rest_framework/schemas.py @@ -206,6 +206,9 @@ class SchemaGenerator(object): else: encoding = None + if self.url and path.startswith('/'): + path = path[1:] + return coreapi.Link( url=urlparse.urljoin(self.url, path), action=method.lower(), From aa349fe76729dbea1b8becf1846ce58c70871f35 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 1 Aug 2016 16:14:26 +0100 Subject: [PATCH 049/175] Handle non-string input for IP fields (#4338) --- rest_framework/fields.py | 5 ++++- tests/test_fields.py | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 46d7ed09b..69cf9740b 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -804,7 +804,10 @@ class IPAddressField(CharField): self.validators.extend(validators) def to_internal_value(self, data): - if data and ':' in data: + if not isinstance(data, six.string_types): + self.fail('invalid', value=data) + + if ':' in data: try: if self.protocol in ('both', 'ipv6'): return clean_ipv6_address(data, self.unpack_ipv4) diff --git a/tests/test_fields.py b/tests/test_fields.py index 1149cc4b3..24ff25588 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -663,6 +663,7 @@ class TestIPAddressField(FieldValues): '127.122.111.2231': ['Enter a valid IPv4 or IPv6 address.'], '2001:::9652': ['Enter a valid IPv4 or IPv6 address.'], '2001:0db8:85a3:0042:1000:8a2e:0370:73341': ['Enter a valid IPv4 or IPv6 address.'], + 1000: ['Enter a valid IPv4 or IPv6 address.'], } outputs = {} field = serializers.IPAddressField() From 46a44e52aa8a0eae82cc9c1e290a83ecf156f81a Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 1 Aug 2016 17:15:41 +0100 Subject: [PATCH 050/175] Quantize incoming digitals (#4339) --- rest_framework/fields.py | 5 +++-- tests/test_fields.py | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 69cf9740b..cbf12010c 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -955,7 +955,7 @@ class DecimalField(Field): if value in (decimal.Decimal('Inf'), decimal.Decimal('-Inf')): self.fail('invalid') - return self.validate_precision(value) + return self.quantize(self.validate_precision(value)) def validate_precision(self, value): """ @@ -1018,7 +1018,8 @@ class DecimalField(Field): context.prec = self.max_digits return value.quantize( decimal.Decimal('.1') ** self.decimal_places, - context=context) + context=context + ) # Date & time fields... diff --git a/tests/test_fields.py b/tests/test_fields.py index 24ff25588..105a51973 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -912,6 +912,26 @@ class TestLocalizedDecimalField(TestCase): self.assertTrue(isinstance(field.to_representation(Decimal('1.1')), six.string_types)) +class TestQuantizedValueForDecimal(TestCase): + def test_int_quantized_value_for_decimal(self): + field = serializers.DecimalField(max_digits=4, decimal_places=2) + value = field.to_internal_value(12).as_tuple() + expected_digit_tuple = (0, (1, 2, 0, 0), -2) + self.assertEqual(value, expected_digit_tuple) + + def test_string_quantized_value_for_decimal(self): + field = serializers.DecimalField(max_digits=4, decimal_places=2) + value = field.to_internal_value('12').as_tuple() + expected_digit_tuple = (0, (1, 2, 0, 0), -2) + self.assertEqual(value, expected_digit_tuple) + + def test_part_precision_string_quantized_value_for_decimal(self): + field = serializers.DecimalField(max_digits=4, decimal_places=2) + value = field.to_internal_value('12.0').as_tuple() + expected_digit_tuple = (0, (1, 2, 0, 0), -2) + self.assertEqual(value, expected_digit_tuple) + + class TestNoDecimalPlaces(FieldValues): valid_inputs = { '0.12345': Decimal('0.12345'), From 3ef3fee92627d832962d1e5aed02c19a3eaa554b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 1 Aug 2016 18:44:58 +0100 Subject: [PATCH 051/175] Descriptive error from FileUploadParser when filename not included. (#4340) * Descriptive error from FileUploadParser when filename not included. * Consistent handling of upload filenames --- rest_framework/parsers.py | 15 +++++++++++---- tests/test_parsers.py | 25 ++++++++++++++++++++++--- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py index ab74a6e58..238382364 100644 --- a/rest_framework/parsers.py +++ b/rest_framework/parsers.py @@ -118,6 +118,10 @@ class FileUploadParser(BaseParser): Parser for file upload data. """ media_type = '*/*' + errors = { + 'unhandled': 'FileUpload parse error - none of upload handlers can handle the stream', + 'no_filename': 'Missing filename. Request should include a Content-Disposition header with a filename parameter.', + } def parse(self, stream, media_type=None, parser_context=None): """ @@ -134,6 +138,9 @@ class FileUploadParser(BaseParser): upload_handlers = request.upload_handlers filename = self.get_filename(stream, media_type, parser_context) + if not filename: + raise ParseError(self.errors['no_filename']) + # Note that this code is extracted from Django's handling of # file uploads in MultiPartParser. content_type = meta.get('HTTP_CONTENT_TYPE', @@ -146,7 +153,7 @@ class FileUploadParser(BaseParser): # See if the handler will want to take care of the parsing. for handler in upload_handlers: - result = handler.handle_raw_input(None, + result = handler.handle_raw_input(stream, meta, content_length, None, @@ -178,10 +185,10 @@ class FileUploadParser(BaseParser): for index, handler in enumerate(upload_handlers): file_obj = handler.file_complete(counters[index]) - if file_obj: + if file_obj is not None: return DataAndFiles({}, {'file': file_obj}) - raise ParseError("FileUpload parse error - " - "none of upload handlers can handle the stream") + + raise ParseError(self.errors['unhandled']) def get_filename(self, stream, media_type, parser_context): """ diff --git a/tests/test_parsers.py b/tests/test_parsers.py index 1e0f2e17f..f3af6817f 100644 --- a/tests/test_parsers.py +++ b/tests/test_parsers.py @@ -2,8 +2,11 @@ from __future__ import unicode_literals +import pytest from django import forms -from django.core.files.uploadhandler import MemoryFileUploadHandler +from django.core.files.uploadhandler import ( + MemoryFileUploadHandler, TemporaryFileUploadHandler +) from django.test import TestCase from django.utils.six.moves import StringIO @@ -63,8 +66,9 @@ class TestFileUploadParser(TestCase): parser = FileUploadParser() self.stream.seek(0) self.parser_context['request'].META['HTTP_CONTENT_DISPOSITION'] = '' - with self.assertRaises(ParseError): + with pytest.raises(ParseError) as excinfo: parser.parse(self.stream, None, self.parser_context) + assert str(excinfo.value) == 'Missing filename. Request should include a Content-Disposition header with a filename parameter.' def test_parse_missing_filename_multiple_upload_handlers(self): """ @@ -78,8 +82,23 @@ class TestFileUploadParser(TestCase): MemoryFileUploadHandler() ) self.parser_context['request'].META['HTTP_CONTENT_DISPOSITION'] = '' - with self.assertRaises(ParseError): + with pytest.raises(ParseError) as excinfo: parser.parse(self.stream, None, self.parser_context) + assert str(excinfo.value) == 'Missing filename. Request should include a Content-Disposition header with a filename parameter.' + + def test_parse_missing_filename_large_file(self): + """ + Parse raw file upload when filename is missing with TemporaryFileUploadHandler. + """ + parser = FileUploadParser() + self.stream.seek(0) + self.parser_context['request'].upload_handlers = ( + TemporaryFileUploadHandler(), + ) + self.parser_context['request'].META['HTTP_CONTENT_DISPOSITION'] = '' + with pytest.raises(ParseError) as excinfo: + parser.parse(self.stream, None, self.parser_context) + assert str(excinfo.value) == 'Missing filename. Request should include a Content-Disposition header with a filename parameter.' def test_get_filename(self): parser = FileUploadParser() From 296e47a9f8f5303d7862b68db7277aa87886e8a9 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 2 Aug 2016 10:23:56 +0100 Subject: [PATCH 052/175] Update from Django 1.10 beta to Django 1.10 (#4344) --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 89655aee2..1e8a7e5c4 100644 --- a/tox.ini +++ b/tox.ini @@ -16,8 +16,8 @@ setenv = PYTHONWARNINGS=once deps = django18: Django==1.8.14 - django19: Django==1.9.8 - django110: Django==1.10rc1 + django19: Django==1.9.9 + django110: Django==1.10 djangomaster: https://github.com/django/django/archive/master.tar.gz -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt From e37619f7410bcfc472db56635aa573b33f83e92a Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 2 Aug 2016 13:05:12 +0100 Subject: [PATCH 053/175] Serializer defaults should not be included in partial updates. (#4346) Serializer default values should not be included in partial updates --- docs/api-guide/fields.md | 4 +++- rest_framework/fields.py | 3 ++- tests/test_serializer.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index a7ad4f70c..4b566d37e 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -49,7 +49,9 @@ Defaults to `False` ### `default` -If set, this gives the default value that will be used for the field if no input value is supplied. If not set the default behavior is to not populate the attribute at all. +If set, this gives the default value that will be used for the field if no input value is supplied. If not set the default behaviour is to not populate the attribute at all. + +The `default` is not applied during partial update operations. In the partial update case only fields that are provided in the incoming data will have a validated value returned. May be set to a function or other callable, in which case the value will be evaluated each time it is used. When called, it will receive no arguments. If the callable has a `set_context` method, that will be called each time before getting the value with the field instance as only argument. This works the same way as for [validators](validators.md#using-set_context). diff --git a/rest_framework/fields.py b/rest_framework/fields.py index cbf12010c..704dbaf3f 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -435,7 +435,8 @@ class Field(object): return `empty`, indicating that no value should be set in the validated data for this field. """ - if self.default is empty: + if self.default is empty or getattr(self.root, 'partial', False): + # No default, or this is a partial update. raise SkipField() if callable(self.default): if hasattr(self.default, 'set_context'): diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 741c6ab17..4e9080909 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -309,3 +309,31 @@ class TestCacheSerializerData: pickled = pickle.dumps(serializer.data) data = pickle.loads(pickled) assert data == {'field1': 'a', 'field2': 'b'} + + +class TestDefaultInclusions: + def setup(self): + class ExampleSerializer(serializers.Serializer): + char = serializers.CharField(read_only=True, default='abc') + integer = serializers.IntegerField() + self.Serializer = ExampleSerializer + + def test_default_should_included_on_create(self): + serializer = self.Serializer(data={'integer': 456}) + assert serializer.is_valid() + assert serializer.validated_data == {'char': 'abc', 'integer': 456} + assert serializer.errors == {} + + def test_default_should_be_included_on_update(self): + instance = MockObject(char='def', integer=123) + serializer = self.Serializer(instance, data={'integer': 456}) + assert serializer.is_valid() + assert serializer.validated_data == {'char': 'abc', 'integer': 456} + assert serializer.errors == {} + + def test_default_should_not_be_included_on_partial_update(self): + instance = MockObject(char='def', integer=123) + serializer = self.Serializer(instance, data={'integer': 456}, partial=True) + assert serializer.is_valid() + assert serializer.validated_data == {'integer': 456} + assert serializer.errors == {} From 9f5e841daf8e086de2b2b90153356a52ce783283 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fleschenberg?= Date: Tue, 2 Aug 2016 14:11:41 +0200 Subject: [PATCH 054/175] Change template context generation in TemplateHTMLRenderer (#4236) - Change the name of ``resolve_context()`` to ``get_template_context()``. - Pass the renderer context to this method, to give subclasses more flexibility when overriding. --- rest_framework/renderers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index e313998d1..ef7747eaf 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -166,13 +166,14 @@ class TemplateHTMLRenderer(BaseRenderer): template_names = self.get_template_names(response, view) template = self.resolve_template(template_names) - context = self.resolve_context(data, request, response) + context = self.get_template_context(data, renderer_context) return template_render(template, context, request=request) def resolve_template(self, template_names): return loader.select_template(template_names) - def resolve_context(self, data, request, response): + def get_template_context(self, data, renderer_context): + response = renderer_context['response'] if response.exception: data['status_code'] = response.status_code return data From bda16a518a03ca79c912c8b90adfce3626cf1069 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 2 Aug 2016 13:33:14 +0100 Subject: [PATCH 055/175] Dedent tabs. (#4347) --- rest_framework/utils/formatting.py | 13 +++++++++++-- tests/test_description.py | 5 +++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/rest_framework/utils/formatting.py b/rest_framework/utils/formatting.py index 67aabd3f1..ca5b33c5e 100644 --- a/rest_framework/utils/formatting.py +++ b/rest_framework/utils/formatting.py @@ -32,13 +32,22 @@ def dedent(content): unindented text on the initial line. """ content = force_text(content) - whitespace_counts = [len(line) - len(line.lstrip(' ')) - for line in content.splitlines()[1:] if line.lstrip()] + whitespace_counts = [ + len(line) - len(line.lstrip(' ')) + for line in content.splitlines()[1:] if line.lstrip() + ] + tab_counts = [ + len(line) - len(line.lstrip('\t')) + for line in content.splitlines()[1:] if line.lstrip() + ] # unindent the content if needed if whitespace_counts: whitespace_pattern = '^' + (' ' * min(whitespace_counts)) content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', content) + elif tab_counts: + whitespace_pattern = '^' + ('\t' * min(whitespace_counts)) + content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', content) return content.strip() diff --git a/tests/test_description.py b/tests/test_description.py index 1683e106b..fcb88287b 100644 --- a/tests/test_description.py +++ b/tests/test_description.py @@ -6,6 +6,7 @@ from django.test import TestCase from django.utils.encoding import python_2_unicode_compatible from rest_framework.compat import apply_markdown +from rest_framework.utils.formatting import dedent from rest_framework.views import APIView @@ -120,3 +121,7 @@ class TestViewNamesAndDescriptions(TestCase): gte_21_match = apply_markdown(DESCRIPTION) == MARKED_DOWN_gte_21 lt_21_match = apply_markdown(DESCRIPTION) == MARKED_DOWN_lt_21 self.assertTrue(gte_21_match or lt_21_match) + + +def test_dedent_tabs(): + assert dedent("\tfirst string\n\n\tsecond string") == 'first string\n\n\tsecond string' From 5500b265bceb1d964725d3fd13f60429b834dbf4 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 2 Aug 2016 14:14:36 +0100 Subject: [PATCH 056/175] Test cases for DictField with allow_null options (#4348) --- tests/test_fields.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/test_fields.py b/tests/test_fields.py index 105a51973..92f4548e5 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1594,6 +1594,29 @@ class TestDictField(FieldValues): "Remove `source=` from the field declaration." ) + def test_allow_null(self): + """ + If `allow_null=True` then `None` is a valid input. + """ + field = serializers.DictField(allow_null=True) + output = field.run_validation(None) + assert output is None + + +class TestDictFieldWithNullChild(FieldValues): + """ + Values for `ListField` with allow_null CharField as child. + """ + valid_inputs = [ + ({'a': None, 'b': '2', 3: 3}, {'a': None, 'b': '2', '3': '3'}), + ] + invalid_inputs = [ + ] + outputs = [ + ({'a': None, 'b': '2', 3: 3}, {'a': None, 'b': '2', '3': '3'}), + ] + field = serializers.DictField(child=serializers.CharField(allow_null=True)) + class TestUnvalidatedDictField(FieldValues): """ From a9a097496ec86d4b5e7db756fe51a39a57f5b370 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 2 Aug 2016 14:33:15 +0100 Subject: [PATCH 057/175] extra_kwargs takes precedence over uniqueness kwargs (#4349) --- rest_framework/serializers.py | 5 ++--- tests/test_model_serializer.py | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index d5e6b66ed..27c8cc229 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -1324,9 +1324,8 @@ class ModelSerializer(Serializer): # 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 + value.update(extra_kwargs[key]) + extra_kwargs[key] = value return extra_kwargs, hidden_fields diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 2cf6cb04c..b2d336d84 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -976,3 +976,22 @@ class TestModelFieldValues(TestCase): source = OneToOneSourceTestModel(target=target) serializer = ExampleSerializer(source) self.assertEqual(serializer.data, {'target': 1}) + + +class TestUniquenessOverride(TestCase): + def test_required_not_overwritten(self): + class TestModel(models.Model): + field_1 = models.IntegerField(null=True) + field_2 = models.IntegerField() + + class Meta: + unique_together = (('field_1', 'field_2'),) + + class TestSerializer(serializers.ModelSerializer): + class Meta: + model = TestModel + extra_kwargs = {'field_1': {'required': False}} + + fields = TestSerializer().fields + self.assertFalse(fields['field_1'].required) + self.assertTrue(fields['field_2'].required) From 54096dc22f255140f148906e85da5e4be0048f10 Mon Sep 17 00:00:00 2001 From: Corentin Smith Date: Thu, 4 Aug 2016 23:06:35 +0200 Subject: [PATCH 058/175] Add imports in validators docs (#4355) --- docs/api-guide/validators.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/validators.md b/docs/api-guide/validators.md index a059f1197..f04c74c3c 100644 --- a/docs/api-guide/validators.md +++ b/docs/api-guide/validators.md @@ -64,6 +64,8 @@ It takes a single required argument, and an optional `messages` argument: This validator should be applied to *serializer fields*, like so: + from rest_framework.validators import UniqueValidator + slug = SlugField( max_length=100, validators=[UniqueValidator(queryset=BlogPost.objects.all())] @@ -80,6 +82,8 @@ It has two required arguments, and a single optional `messages` argument: The validator should be applied to *serializer classes*, like so: + from rest_framework.validators import UniqueTogetherValidator + class ExampleSerializer(serializers.Serializer): # ... class Meta: @@ -114,6 +118,8 @@ These validators can be used to enforce the `unique_for_date`, `unique_for_month The validator should be applied to *serializer classes*, like so: + from rest_framework.validators import UniqueForYearValidator + class ExampleSerializer(serializers.Serializer): # ... class Meta: @@ -183,7 +189,7 @@ It takes a single argument, which is the default value or callable that should b created_at = serializers.DateTimeField( read_only=True, - default=CreateOnlyDefault(timezone.now) + default=serializers.CreateOnlyDefault(timezone.now) ) --- From aff146ae83a64676cdcaf169832c29f4132cac6a Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 5 Aug 2016 10:23:40 +0100 Subject: [PATCH 059/175] Filter HEAD out from schemas (#4357) --- rest_framework/schemas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py index 02960083c..b5d2e0254 100644 --- a/rest_framework/schemas.py +++ b/rest_framework/schemas.py @@ -167,7 +167,7 @@ class SchemaGenerator(object): return [ method for method in - callback.cls().allowed_methods if method != 'OPTIONS' + callback.cls().allowed_methods if method not in ('OPTIONS', 'HEAD') ] def get_key(self, path, method, callback): From 11a2468379496a4e9ed29d84d135825a927a5595 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 5 Aug 2016 11:04:01 +0100 Subject: [PATCH 060/175] Access `request.user.is_authenticated` as property not method, under Django 1.10+ (#4358) * For Django >=1.10 use user.is_authenticated, not user.is_authenticated() --- rest_framework/compat.py | 6 ++++++ rest_framework/permissions.py | 9 ++++++--- rest_framework/throttling.py | 7 ++++--- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 3143e7654..1ab1478f1 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -122,6 +122,12 @@ def _resolve_model(obj): raise ValueError("{0} is not a Django model".format(obj)) +def is_authenticated(user): + if django.VERSION < (1, 10): + return user.is_authenticated() + return user.is_authenticated + + def get_related_model(field): if django.VERSION < (1, 9): return _resolve_model(field.rel.to) diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py index 8f5de0256..dd2d35ccd 100644 --- a/rest_framework/permissions.py +++ b/rest_framework/permissions.py @@ -5,6 +5,9 @@ from __future__ import unicode_literals from django.http import Http404 +from rest_framework.compat import is_authenticated + + SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS') @@ -44,7 +47,7 @@ class IsAuthenticated(BasePermission): """ def has_permission(self, request, view): - return request.user and request.user.is_authenticated() + return request.user and is_authenticated(request.user) class IsAdminUser(BasePermission): @@ -65,7 +68,7 @@ class IsAuthenticatedOrReadOnly(BasePermission): return ( request.method in SAFE_METHODS or request.user and - request.user.is_authenticated() + is_authenticated(request.user) ) @@ -127,7 +130,7 @@ class DjangoModelPermissions(BasePermission): return ( request.user and - (request.user.is_authenticated() or not self.authenticated_users_only) and + (is_authenticated(request.user) or not self.authenticated_users_only) and request.user.has_perms(perms) ) diff --git a/rest_framework/throttling.py b/rest_framework/throttling.py index 1449f501b..57f24d13f 100644 --- a/rest_framework/throttling.py +++ b/rest_framework/throttling.py @@ -8,6 +8,7 @@ import time from django.core.cache import cache as default_cache from django.core.exceptions import ImproperlyConfigured +from rest_framework.compat import is_authenticated from rest_framework.settings import api_settings @@ -173,7 +174,7 @@ class AnonRateThrottle(SimpleRateThrottle): scope = 'anon' def get_cache_key(self, request, view): - if request.user.is_authenticated(): + if is_authenticated(request.user): return None # Only throttle unauthenticated requests. return self.cache_format % { @@ -193,7 +194,7 @@ class UserRateThrottle(SimpleRateThrottle): scope = 'user' def get_cache_key(self, request, view): - if request.user.is_authenticated(): + if is_authenticated(request.user): ident = request.user.pk else: ident = self.get_ident(request) @@ -241,7 +242,7 @@ class ScopedRateThrottle(SimpleRateThrottle): Otherwise generate the unique cache key by concatenating the user id with the '.throttle_scope` property of the view. """ - if request.user.is_authenticated(): + if is_authenticated(request.user): ident = request.user.pk else: ident = self.get_ident(request) From d5178c92462f6d18337fb0d85b6f230e812ed2fb Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 5 Aug 2016 11:19:39 +0100 Subject: [PATCH 061/175] Include kwargs passed to 'as_view' when generating schemas (#4359) --- rest_framework/schemas.py | 2 ++ rest_framework/viewsets.py | 1 + tests/test_schemas.py | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py index b5d2e0254..688deec88 100644 --- a/rest_framework/schemas.py +++ b/rest_framework/schemas.py @@ -195,6 +195,8 @@ class SchemaGenerator(object): Return a `coreapi.Link` instance for the given endpoint. """ view = callback.cls() + for attr, val in getattr(callback, 'initkwargs', {}).items(): + setattr(view, attr, val) fields = self.get_path_fields(path, method, callback, view) fields += self.get_serializer_fields(path, method, callback, view) diff --git a/rest_framework/viewsets.py b/rest_framework/viewsets.py index 7687448c4..2f440c567 100644 --- a/rest_framework/viewsets.py +++ b/rest_framework/viewsets.py @@ -97,6 +97,7 @@ class ViewSetMixin(object): # generation can pick out these bits of information from a # resolved URL. view.cls = cls + view.initkwargs = initkwargs view.suffix = initkwargs.get('suffix', None) view.actions = actions return csrf_exempt(view) diff --git a/tests/test_schemas.py b/tests/test_schemas.py index a32b8a117..6c02c9d23 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -5,6 +5,7 @@ from django.test import TestCase, override_settings from rest_framework import filters, pagination, permissions, serializers from rest_framework.compat import coreapi +from rest_framework.decorators import detail_route from rest_framework.response import Response from rest_framework.routers import DefaultRouter from rest_framework.schemas import SchemaGenerator @@ -27,12 +28,21 @@ class ExampleSerializer(serializers.Serializer): b = serializers.CharField(required=False) +class AnotherSerializer(serializers.Serializer): + c = serializers.CharField(required=True) + d = serializers.CharField(required=False) + + class ExampleViewSet(ModelViewSet): pagination_class = ExamplePagination permission_classes = [permissions.IsAuthenticatedOrReadOnly] filter_backends = [filters.OrderingFilter] serializer_class = ExampleSerializer + @detail_route(methods=['post'], serializer_class=AnotherSerializer) + def custom_action(self, request, pk): + return super(ExampleSerializer, self).retrieve(self, request) + class ExampleView(APIView): permission_classes = [permissions.IsAuthenticatedOrReadOnly] @@ -120,6 +130,16 @@ class TestRouterGeneratedSchema(TestCase): coreapi.Field('pk', required=True, location='path') ] ), + 'custom_action': coreapi.Link( + url='/example/{pk}/custom_action/', + action='post', + encoding='application/json', + fields=[ + coreapi.Field('pk', required=True, location='path'), + coreapi.Field('c', required=True, location='form'), + coreapi.Field('d', required=False, location='form'), + ] + ), 'update': coreapi.Link( url='/example/{pk}/', action='put', From f9cf22edc88a19e83b70fc72de7844c948023f44 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 5 Aug 2016 12:38:19 +0100 Subject: [PATCH 062/175] Version 3.4.2 (#4360) --- docs/topics/release-notes.md | 47 ++++++++++++++++++++++++++++++++++++ rest_framework/__init__.py | 2 +- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 025231ccf..7baff2277 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,6 +40,24 @@ You can determine your currently installed version using `pip freeze`: ## 3.4.x series +### 3.4.2 + +**Date**: [5th August 2016][3.4.2-milestone] + +Include kwargs passed to 'as_view' when generating schemas. ([#4359][gh4359], [#4330][gh4330], [#4331][gh4331]) +Access `request.user.is_authenticated` as property not method, under Django 1.10+ ([#4358][gh4358], [#4354][gh4354]) +Filter HEAD out from schemas. ([#4357][gh4357]) +extra_kwargs takes precedence over uniqueness kwargs. ([#4198][gh4198], [#4199][gh4199], [#4349][gh4349]) +Correct descriptions when tabs are used in code indentation. ([#4345][gh4345], [#4347][gh4347]) +Change template context generation in TemplateHTMLRenderer. ([#4236][gh4236]) +Serializer defaults should not be included in partial updates. ([#4346][gh4346], [#3565][gh3565]) +Consistent behavior & descriptive error from FileUploadParser when filename not included. ([#4340][gh4340], [#3610][gh3610], [#4292][gh4292], [#4296][gh4296]) +DecimalField quantizes incoming digitals. ([#4339][gh4339], [#4318][gh4318]) +Handle non-string input for IP fields. ([#4335][gh4335], [#4336][gh4336], [#4338][gh4338]) +Fix leading slash handling when Schema generation includes a root URL. ([#4332][gh4332]) +Test cases for DictField with allow_null options. ([#4348][gh4348]) +Update tests from Django 1.10 beta to Django 1.10. ([#4344][gh4344]) + ### 3.4.1 **Date**: [28th July 2016][3.4.1-milestone] @@ -514,6 +532,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.3.3-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.3.3+Release%22 [3.4.0-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.0+Release%22 [3.4.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.1+Release%22 +[3.4.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.2+Release%22 [gh2013]: https://github.com/tomchristie/django-rest-framework/issues/2013 @@ -943,3 +962,31 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh4272]: https://github.com/tomchristie/django-rest-framework/issues/4272 [gh4273]: https://github.com/tomchristie/django-rest-framework/issues/4273 [gh4288]: https://github.com/tomchristie/django-rest-framework/issues/4288 + + +[gh3565]: https://github.com/tomchristie/django-rest-framework/issues/3565 +[gh3610]: https://github.com/tomchristie/django-rest-framework/issues/3610 +[gh4198]: https://github.com/tomchristie/django-rest-framework/issues/4198 +[gh4199]: https://github.com/tomchristie/django-rest-framework/issues/4199 +[gh4236]: https://github.com/tomchristie/django-rest-framework/issues/4236 +[gh4292]: https://github.com/tomchristie/django-rest-framework/issues/4292 +[gh4296]: https://github.com/tomchristie/django-rest-framework/issues/4296 +[gh4318]: https://github.com/tomchristie/django-rest-framework/issues/4318 +[gh4330]: https://github.com/tomchristie/django-rest-framework/issues/4330 +[gh4331]: https://github.com/tomchristie/django-rest-framework/issues/4331 +[gh4332]: https://github.com/tomchristie/django-rest-framework/issues/4332 +[gh4335]: https://github.com/tomchristie/django-rest-framework/issues/4335 +[gh4336]: https://github.com/tomchristie/django-rest-framework/issues/4336 +[gh4338]: https://github.com/tomchristie/django-rest-framework/issues/4338 +[gh4339]: https://github.com/tomchristie/django-rest-framework/issues/4339 +[gh4340]: https://github.com/tomchristie/django-rest-framework/issues/4340 +[gh4344]: https://github.com/tomchristie/django-rest-framework/issues/4344 +[gh4345]: https://github.com/tomchristie/django-rest-framework/issues/4345 +[gh4346]: https://github.com/tomchristie/django-rest-framework/issues/4346 +[gh4347]: https://github.com/tomchristie/django-rest-framework/issues/4347 +[gh4348]: https://github.com/tomchristie/django-rest-framework/issues/4348 +[gh4349]: https://github.com/tomchristie/django-rest-framework/issues/4349 +[gh4354]: https://github.com/tomchristie/django-rest-framework/issues/4354 +[gh4357]: https://github.com/tomchristie/django-rest-framework/issues/4357 +[gh4358]: https://github.com/tomchristie/django-rest-framework/issues/4358 +[gh4359]: https://github.com/tomchristie/django-rest-framework/issues/4359 diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 5c61f8d7f..fb6da68ce 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ ______ _____ _____ _____ __ """ __title__ = 'Django REST framework' -__version__ = '3.4.1' +__version__ = '3.4.2' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2016 Tom Christie' From 35320b1f2d07fcc20b21f5ebc71629c1fc4ebb21 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 5 Aug 2016 12:41:15 +0100 Subject: [PATCH 063/175] Add bullet points to release notes [ci skip] --- docs/topics/release-notes.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 7baff2277..692af57e9 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -44,19 +44,19 @@ You can determine your currently installed version using `pip freeze`: **Date**: [5th August 2016][3.4.2-milestone] -Include kwargs passed to 'as_view' when generating schemas. ([#4359][gh4359], [#4330][gh4330], [#4331][gh4331]) -Access `request.user.is_authenticated` as property not method, under Django 1.10+ ([#4358][gh4358], [#4354][gh4354]) -Filter HEAD out from schemas. ([#4357][gh4357]) -extra_kwargs takes precedence over uniqueness kwargs. ([#4198][gh4198], [#4199][gh4199], [#4349][gh4349]) -Correct descriptions when tabs are used in code indentation. ([#4345][gh4345], [#4347][gh4347]) -Change template context generation in TemplateHTMLRenderer. ([#4236][gh4236]) -Serializer defaults should not be included in partial updates. ([#4346][gh4346], [#3565][gh3565]) -Consistent behavior & descriptive error from FileUploadParser when filename not included. ([#4340][gh4340], [#3610][gh3610], [#4292][gh4292], [#4296][gh4296]) -DecimalField quantizes incoming digitals. ([#4339][gh4339], [#4318][gh4318]) -Handle non-string input for IP fields. ([#4335][gh4335], [#4336][gh4336], [#4338][gh4338]) -Fix leading slash handling when Schema generation includes a root URL. ([#4332][gh4332]) -Test cases for DictField with allow_null options. ([#4348][gh4348]) -Update tests from Django 1.10 beta to Django 1.10. ([#4344][gh4344]) +* Include kwargs passed to 'as_view' when generating schemas. ([#4359][gh4359], [#4330][gh4330], [#4331][gh4331]) +* Access `request.user.is_authenticated` as property not method, under Django 1.10+ ([#4358][gh4358], [#4354][gh4354]) +* Filter HEAD out from schemas. ([#4357][gh4357]) +* extra_kwargs takes precedence over uniqueness kwargs. ([#4198][gh4198], [#4199][gh4199], [#4349][gh4349]) +* Correct descriptions when tabs are used in code indentation. ([#4345][gh4345], [#4347][gh4347])* +* Change template context generation in TemplateHTMLRenderer. ([#4236][gh4236]) +* Serializer defaults should not be included in partial updates. ([#4346][gh4346], [#3565][gh3565]) +* Consistent behavior & descriptive error from FileUploadParser when filename not included. ([#4340][gh4340], [#3610][gh3610], [#4292][gh4292], [#4296][gh4296]) +* DecimalField quantizes incoming digitals. ([#4339][gh4339], [#4318][gh4318]) +* Handle non-string input for IP fields. ([#4335][gh4335], [#4336][gh4336], [#4338][gh4338]) +* Fix leading slash handling when Schema generation includes a root URL. ([#4332][gh4332]) +* Test cases for DictField with allow_null options. ([#4348][gh4348]) +* Update tests from Django 1.10 beta to Django 1.10. ([#4344][gh4344]) ### 3.4.1 From bb613c5ad19bb10a8e85a2bb3520bd4c4e6879a5 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 5 Aug 2016 13:33:25 +0100 Subject: [PATCH 064/175] Version 3.4.3 (#4361) * Version 3.4.3 --- docs/topics/release-notes.md | 10 ++++++++++ rest_framework/__init__.py | 2 +- rest_framework/renderers.py | 11 +++++++++-- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 692af57e9..6edbd2544 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,6 +40,12 @@ You can determine your currently installed version using `pip freeze`: ## 3.4.x series +### 3.4.3 + +**Date**: [5th August 2016][3.4.3-milestone] + +* Include fallaback for users of older TemplateHTMLRenderer internal API. ([#4361][gh4361]) + ### 3.4.2 **Date**: [5th August 2016][3.4.2-milestone] @@ -533,6 +539,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.4.0-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.0+Release%22 [3.4.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.1+Release%22 [3.4.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.2+Release%22 +[3.4.3-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.3+Release%22 [gh2013]: https://github.com/tomchristie/django-rest-framework/issues/2013 @@ -990,3 +997,6 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh4357]: https://github.com/tomchristie/django-rest-framework/issues/4357 [gh4358]: https://github.com/tomchristie/django-rest-framework/issues/4358 [gh4359]: https://github.com/tomchristie/django-rest-framework/issues/4359 + + +[gh4361]: https://github.com/tomchristie/django-rest-framework/issues/4361 diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index fb6da68ce..19f83ecab 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ ______ _____ _____ _____ __ """ __title__ = 'Django REST framework' -__version__ = '3.4.2' +__version__ = '3.4.3' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2016 Tom Christie' diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index ef7747eaf..91d9e9072 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -166,7 +166,11 @@ class TemplateHTMLRenderer(BaseRenderer): template_names = self.get_template_names(response, view) template = self.resolve_template(template_names) - context = self.get_template_context(data, renderer_context) + if hasattr(self, 'resolve_context'): + # Fallback for older versions. + context = self.resolve_context(self, data, request, response) + else: + context = self.get_template_context(data, renderer_context) return template_render(template, context, request=request) def resolve_template(self, template_names): @@ -229,7 +233,10 @@ class StaticHTMLRenderer(TemplateHTMLRenderer): if response and response.exception: request = renderer_context['request'] template = self.get_exception_template(response) - context = self.resolve_context(data, request, response) + if hasattr(self, 'resolve_context'): + context = self.resolve_context(data, request, response) + else: + context = self.get_template_context(data, renderer_context) return template_render(template, context, request=request) return data From 672e5a0f96c4e860f4eeee85f9cd26c8c4070d63 Mon Sep 17 00:00:00 2001 From: Marlon Date: Fri, 5 Aug 2016 11:57:43 -0700 Subject: [PATCH 065/175] Fix minor typo --- docs/api-guide/fields.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 4b566d37e..f986f1508 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -488,7 +488,7 @@ This field is used by default with `ModelSerializer` when including field names **Signature**: `ReadOnlyField()` -For example, is `has_expired` was a property on the `Account` model, then the following serializer would automatically generate it as a `ReadOnlyField`: +For example, if `has_expired` was a property on the `Account` model, then the following serializer would automatically generate it as a `ReadOnlyField`: class AccountSerializer(serializers.ModelSerializer): class Meta: From febaa4db00c322d169a5e60ecd22c909e8a836c2 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 8 Aug 2016 09:28:15 +0100 Subject: [PATCH 066/175] Add import in docs. [ci skip] --- docs/api-guide/throttling.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 4eef4fd5d..51d2beef1 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -184,6 +184,8 @@ If the `.wait()` method is implemented and the request is throttled, then a `Ret The following is an example of a rate throttle, that will randomly throttle 1 in every 10 requests. + import random + class RandomRateThrottle(throttling.BaseThrottle): def allow_request(self, request, view): return random.randint(1, 10) == 1 From e1768bdc161ee6c4fab422d0f47e54f9bf257ce2 Mon Sep 17 00:00:00 2001 From: Dmitry Dygalo Date: Mon, 8 Aug 2016 10:32:22 +0200 Subject: [PATCH 067/175] Fixed various typos (#4366) --- rest_framework/compat.py | 2 +- rest_framework/fields.py | 6 +++--- rest_framework/negotiation.py | 2 +- rest_framework/pagination.py | 16 ++++++++-------- rest_framework/serializers.py | 4 ++-- rest_framework/templatetags/rest_framework.py | 2 +- rest_framework/utils/field_mapping.py | 2 +- rest_framework/utils/html.py | 2 +- rest_framework/versioning.py | 4 ++-- rest_framework/viewsets.py | 2 +- tests/test_authentication.py | 2 +- tests/test_fields.py | 4 ++-- tests/test_filters.py | 6 +++--- tests/test_model_serializer.py | 2 +- tests/test_pagination.py | 6 +++--- tests/test_templatetags.py | 2 +- tests/test_testing.py | 2 +- 17 files changed, 33 insertions(+), 33 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 1ab1478f1..cee430a84 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -168,7 +168,7 @@ except ImportError: crispy_forms = None -# coreapi is optional (Note that uritemplate is a dependancy of coreapi) +# coreapi is optional (Note that uritemplate is a dependency of coreapi) try: import coreapi import uritemplate diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 704dbaf3f..b4346cd85 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -395,8 +395,8 @@ class Field(object): # determine if we should use null instead. return '' if getattr(self, 'allow_blank', False) else None elif ret == '' and not self.required: - # If the field is blank, and emptyness is valid then - # determine if we should use emptyness instead. + # If the field is blank, and emptiness is valid then + # determine if we should use emptiness instead. return '' if getattr(self, 'allow_blank', False) else empty return ret return dictionary.get(self.field_name, empty) @@ -1346,7 +1346,7 @@ class FilePathField(ChoiceField): def __init__(self, path, match=None, recursive=False, allow_files=True, allow_folders=False, required=None, **kwargs): - # Defer to Django's FilePathField implmentation to get the + # Defer to Django's FilePathField implementation to get the # valid set of choices. field = DjangoFilePathField( path, match=match, recursive=recursive, allow_files=allow_files, diff --git a/rest_framework/negotiation.py b/rest_framework/negotiation.py index 2a2b6f168..ca1b59f12 100644 --- a/rest_framework/negotiation.py +++ b/rest_framework/negotiation.py @@ -90,7 +90,7 @@ class DefaultContentNegotiation(BaseContentNegotiation): def get_accept_list(self, request): """ - Given the incoming request, return a tokenised list of media + Given the incoming request, return a tokenized list of media type strings. """ header = request.META.get('HTTP_ACCEPT', '*/*') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 6ad10d860..77c10337e 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -64,10 +64,10 @@ def _get_displayed_page_numbers(current, final): 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 + ensures that any breaks between non-continuous page numbers never remove only a single page. - For an alernativative implementation which gives two pages to each side of + For an alternative 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 @@ -476,10 +476,10 @@ class CursorPagination(BasePagination): # Determine the position of the final item following the page. if len(results) > len(self.page): - has_following_postion = True + has_following_position = True following_position = self._get_position_from_instance(results[-1], self.ordering) else: - has_following_postion = False + has_following_position = False following_position = None # If we have a reverse queryset, then the query ordering was in reverse @@ -490,14 +490,14 @@ class CursorPagination(BasePagination): 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 + self.has_previous = has_following_position 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_next = has_following_position self.has_previous = (current_position is not None) or (offset > 0) if self.has_next: self.next_position = following_position @@ -534,7 +534,7 @@ class CursorPagination(BasePagination): # our marker. break - # The item in this postion has the same position as the item + # The item in this position 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 @@ -582,7 +582,7 @@ class CursorPagination(BasePagination): # our marker. break - # The item in this postion has the same position as the item + # The item in this position 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 diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 27c8cc229..41412af8a 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -1383,7 +1383,7 @@ class ModelSerializer(Serializer): def get_unique_together_validators(self): """ - Determine a default set of validators for any unique_together contraints. + Determine a default set of validators for any unique_together constraints. """ model_class_inheritance_tree = ( [self.Meta.model] + @@ -1414,7 +1414,7 @@ class ModelSerializer(Serializer): def get_unique_for_date_validators(self): """ - Determine a default set of validators for the following contraints: + Determine a default set of validators for the following constraints: * unique_for_date * unique_for_month diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index 05a7ce04f..225edcbee 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -189,7 +189,7 @@ def urlize_quoted_links(text, trim_url_limit=None, nofollow=True, autoescape=Tru leading punctuation (opening parens) and it'll still do the right thing. If trim_url_limit is not None, the URLs in link text longer than this limit - will truncated to trim_url_limit-3 characters and appended with an elipsis. + will truncated to trim_url_limit-3 characters and appended with an ellipsis. If nofollow is True, the URLs in link text will get a rel="nofollow" attribute. diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index 311d58317..64df9a08e 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -1,6 +1,6 @@ """ Helper functions for mapping model fields to a dictionary of default -keyword arguments that should be used for their equivelent serializer fields. +keyword arguments that should be used for their equivalent serializer fields. """ import inspect diff --git a/rest_framework/utils/html.py b/rest_framework/utils/html.py index 121c825c7..ca062c9fc 100644 --- a/rest_framework/utils/html.py +++ b/rest_framework/utils/html.py @@ -14,7 +14,7 @@ def is_html_input(dictionary): def parse_html_list(dictionary, prefix=''): """ - Used to suport list values in HTML forms. + Used to support list values in HTML forms. Supports lists of primitives and/or dictionaries. * List of primitives. diff --git a/rest_framework/versioning.py b/rest_framework/versioning.py index 763a92b4c..f533ef580 100644 --- a/rest_framework/versioning.py +++ b/rest_framework/versioning.py @@ -94,7 +94,7 @@ class NamespaceVersioning(BaseVersioning): 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 + An example URL conf that is namespaced into two separate versions # users/urls.py urlpatterns = [ @@ -147,7 +147,7 @@ class HostNameVersioning(BaseVersioning): invalid_version_message = _('Invalid version in hostname.') def determine_version(self, request, *args, **kwargs): - hostname, seperator, port = request.get_host().partition(':') + hostname, separator, port = request.get_host().partition(':') match = self.hostname_regex.match(hostname) if not match: return self.default_version diff --git a/rest_framework/viewsets.py b/rest_framework/viewsets.py index 2f440c567..bd8333504 100644 --- a/rest_framework/viewsets.py +++ b/rest_framework/viewsets.py @@ -112,7 +112,7 @@ class ViewSetMixin(object): if method == 'options': # This is a special case as we always provide handling for the # options method in the base `View` class. - # Unlike the other explicitly defined actions, 'metadata' is implict. + # Unlike the other explicitly defined actions, 'metadata' is implicit. self.action = 'metadata' else: self.action = self.action_map.get(method) diff --git a/tests/test_authentication.py b/tests/test_authentication.py index 1f95396aa..5ef620abe 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -440,7 +440,7 @@ class FailingAuthAccessedInRenderer(TestCase): class NoAuthenticationClassesTests(TestCase): def test_permission_message_with_no_authentication_classes(self): """ - An unauthenticated request made against a view that containes no + An unauthenticated request made against a view that contains no `authentication_classes` but do contain `permissions_classes` the error code returned should be 403 with the exception's message. """ diff --git a/tests/test_fields.py b/tests/test_fields.py index 92f4548e5..60f02777a 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -974,7 +974,7 @@ class TestDateField(FieldValues): class TestCustomInputFormatDateField(FieldValues): """ - Valid and invalid values for `DateField` with a cutom input format. + Valid and invalid values for `DateField` with a custom input format. """ valid_inputs = { '1 Jan 2001': datetime.date(2001, 1, 1), @@ -1041,7 +1041,7 @@ class TestDateTimeField(FieldValues): class TestCustomInputFormatDateTimeField(FieldValues): """ - Valid and invalid values for `DateTimeField` with a cutom input format. + Valid and invalid values for `DateTimeField` with a custom input format. """ valid_inputs = { '1:35pm, 1 Jan 2001': datetime.datetime(2001, 1, 1, 13, 35, tzinfo=timezone.UTC()), diff --git a/tests/test_filters.py b/tests/test_filters.py index 175ae5b12..03d61fc37 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -711,7 +711,7 @@ class OrderingFilterTests(TestCase): serializer_class = OrderingFilterSerializer filter_backends = (filters.OrderingFilter,) ordering = ('title',) - oredering_fields = ('text',) + ordering_fields = ('text',) view = OrderingListView.as_view() request = factory.get('') @@ -819,7 +819,7 @@ class OrderingFilterTests(TestCase): queryset = OrderingFilterModel.objects.all() filter_backends = (filters.OrderingFilter,) ordering = ('title',) - # note: no ordering_fields and serializer_class speficied + # note: no ordering_fields and serializer_class specified def get_serializer_class(self): return OrderingFilterSerializer @@ -842,7 +842,7 @@ class OrderingFilterTests(TestCase): filter_backends = (filters.OrderingFilter,) ordering = ('title',) # note: no ordering_fields and serializer_class - # or get_serializer_class speficied + # or get_serializer_class specified view = OrderingListView.as_view() request = factory.get('/', {'ordering': 'text'}) diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index b2d336d84..a14972f04 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -136,7 +136,7 @@ class TestModelSerializer(TestCase): class TestRegularFieldMappings(TestCase): def test_regular_fields(self): """ - Model fields should map to their equivelent serializer fields. + Model fields should map to their equivalent serializer fields. """ class TestSerializer(serializers.ModelSerializer): class Meta: diff --git a/tests/test_pagination.py b/tests/test_pagination.py index 4ea706429..f7feb4006 100644 --- a/tests/test_pagination.py +++ b/tests/test_pagination.py @@ -67,8 +67,8 @@ class TestPaginationIntegration: def test_setting_page_size_over_maximum(self): """ - When page_size parameter exceeds maxiumum allowable, - then it should be capped to the maxiumum. + When page_size parameter exceeds maximum allowable, + then it should be capped to the maximum. """ request = factory.get('/', {'page_size': 1000}) response = self.view(request) @@ -259,7 +259,7 @@ class TestPageNumberPaginationOverride: def setup(self): class OverriddenDjangoPaginator(DjangoPaginator): - # override the count in our overriden Django Paginator + # override the count in our overridden Django Paginator # we will only return one page, with one item count = 1 diff --git a/tests/test_templatetags.py b/tests/test_templatetags.py index 746f51b7e..ac218df21 100644 --- a/tests/test_templatetags.py +++ b/tests/test_templatetags.py @@ -13,7 +13,7 @@ factory = APIRequestFactory() class TemplateTagTests(TestCase): - def test_add_query_param_with_non_latin_charactor(self): + def test_add_query_param_with_non_latin_character(self): # Ensure we don't double-escape non-latin characters # that are present in the querystring. # See #1314. diff --git a/tests/test_testing.py b/tests/test_testing.py index e6c8de22d..3adcc55f8 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -78,7 +78,7 @@ class TestAPITestClient(TestCase): response = self.client.get('/session-view/') self.assertEqual(response.data['active_session'], False) - # Subsequant requests have an active session + # Subsequent requests have an active session response = self.client.get('/session-view/') self.assertEqual(response.data['active_session'], True) From 0781182646e4b292e88ec46b74cc5b709c190753 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 9 Aug 2016 17:48:29 +0100 Subject: [PATCH 068/175] Fix call to .resolve_context (#4371) --- rest_framework/renderers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 91d9e9072..371cd6ec7 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -168,7 +168,7 @@ class TemplateHTMLRenderer(BaseRenderer): if hasattr(self, 'resolve_context'): # Fallback for older versions. - context = self.resolve_context(self, data, request, response) + context = self.resolve_context(data, request, response) else: context = self.get_template_context(data, renderer_context) return template_render(template, context, request=request) From 8105a4ac5abc9760ac5dea8e567f333feb6f8e2a Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 10 Aug 2016 12:02:33 +0100 Subject: [PATCH 069/175] Resolve form display with ChoiceField, MultipleChoiceField and non-string choices. (#4374) * Add tests for html-form-rendering choice fields * Resolve issues with ChoiceField, MultipleChoiceField and non-string options * Ensure None template comparisons don't match string None --- .../horizontal/checkbox_multiple.html | 6 +- .../rest_framework/horizontal/radio.html | 6 +- .../rest_framework/horizontal/select.html | 4 +- .../horizontal/select_multiple.html | 4 +- .../inline/checkbox_multiple.html | 4 +- .../rest_framework/inline/radio.html | 3 +- .../rest_framework/inline/select.html | 4 +- .../inline/select_multiple.html | 3 +- .../vertical/checkbox_multiple.html | 6 +- .../rest_framework/vertical/radio.html | 5 +- .../rest_framework/vertical/select.html | 4 +- .../vertical/select_multiple.html | 3 +- rest_framework/templatetags/rest_framework.py | 15 ++++ rest_framework/utils/serializer_helpers.py | 2 +- tests/test_renderers.py | 87 +++++++++++++++++++ 15 files changed, 139 insertions(+), 17 deletions(-) diff --git a/rest_framework/templates/rest_framework/horizontal/checkbox_multiple.html b/rest_framework/templates/rest_framework/horizontal/checkbox_multiple.html index f01071297..7c7e57326 100644 --- a/rest_framework/templates/rest_framework/horizontal/checkbox_multiple.html +++ b/rest_framework/templates/rest_framework/horizontal/checkbox_multiple.html @@ -1,3 +1,5 @@ +{% load rest_framework %} +
{% if field.label %}
@@ -19,7 +21,7 @@ {% for key, text in field.choices.items %} {% endfor %} @@ -35,7 +37,7 @@ {% for key, text in field.choices.items %}
diff --git a/rest_framework/templates/rest_framework/horizontal/select.html b/rest_framework/templates/rest_framework/horizontal/select.html index 3c9e36bbb..7a3db2db8 100644 --- a/rest_framework/templates/rest_framework/horizontal/select.html +++ b/rest_framework/templates/rest_framework/horizontal/select.html @@ -1,3 +1,5 @@ +{% load rest_framework %} +
{% if field.label %}
@@ -16,7 +18,7 @@ {% elif select.end_option_group %} {% else %} - + {% endif %} {% empty %} diff --git a/rest_framework/templates/rest_framework/inline/checkbox_multiple.html b/rest_framework/templates/rest_framework/inline/checkbox_multiple.html index b02425a62..4c544ff8a 100644 --- a/rest_framework/templates/rest_framework/inline/checkbox_multiple.html +++ b/rest_framework/templates/rest_framework/inline/checkbox_multiple.html @@ -1,3 +1,5 @@ +{% load rest_framework %} +
{% if field.label %} @@ -6,7 +8,7 @@ {% for key, text in field.choices.items %}
diff --git a/rest_framework/templates/rest_framework/inline/radio.html b/rest_framework/templates/rest_framework/inline/radio.html index efaef6c9b..8d43cb390 100644 --- a/rest_framework/templates/rest_framework/inline/radio.html +++ b/rest_framework/templates/rest_framework/inline/radio.html @@ -1,4 +1,5 @@ {% load i18n %} +{% load rest_framework %} {% trans "None" as none_choice %}
@@ -20,7 +21,7 @@ {% for key, text in field.choices.items %}
diff --git a/rest_framework/templates/rest_framework/inline/select.html b/rest_framework/templates/rest_framework/inline/select.html index 99f10ae71..5023c2203 100644 --- a/rest_framework/templates/rest_framework/inline/select.html +++ b/rest_framework/templates/rest_framework/inline/select.html @@ -1,3 +1,5 @@ +{% load rest_framework %} +
{% if field.label %}
@@ -15,7 +16,7 @@ {% elif select.end_option_group %} {% else %} - + {% endif %} {% empty %} diff --git a/rest_framework/templates/rest_framework/vertical/checkbox_multiple.html b/rest_framework/templates/rest_framework/vertical/checkbox_multiple.html index 8412f8e2d..b933f4ff5 100644 --- a/rest_framework/templates/rest_framework/vertical/checkbox_multiple.html +++ b/rest_framework/templates/rest_framework/vertical/checkbox_multiple.html @@ -1,3 +1,5 @@ +{% load rest_framework %} +
{% if field.label %} @@ -7,7 +9,7 @@
{% for key, text in field.choices.items %} {% endfor %} @@ -16,7 +18,7 @@ {% for key, text in field.choices.items %}
diff --git a/rest_framework/templates/rest_framework/vertical/radio.html b/rest_framework/templates/rest_framework/vertical/radio.html index 9922ef6a1..6e5d2232c 100644 --- a/rest_framework/templates/rest_framework/vertical/radio.html +++ b/rest_framework/templates/rest_framework/vertical/radio.html @@ -1,4 +1,5 @@ {% load i18n %} +{% load rest_framework %} {% trans "None" as none_choice %}
@@ -19,7 +20,7 @@ {% for key, text in field.choices.items %} {% endfor %} @@ -37,7 +38,7 @@ {% for key, text in field.choices.items %}
diff --git a/rest_framework/templates/rest_framework/vertical/select.html b/rest_framework/templates/rest_framework/vertical/select.html index 9736fc072..6ccaaf27f 100644 --- a/rest_framework/templates/rest_framework/vertical/select.html +++ b/rest_framework/templates/rest_framework/vertical/select.html @@ -1,3 +1,5 @@ +{% load rest_framework %} +
{% if field.label %}
' + ) + rendered_packed = ''.join(rendered.split()) + assert rendered_packed == expected_packed + class TestNestedBoundField: def test_nested_empty_bound_field(self): From f1a2eeb818366b11c44c86d2ccfa1d6822d216b9 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 10 Aug 2016 16:38:59 +0100 Subject: [PATCH 075/175] .choices property of RelatedField should preserve non-string values. (#4379) Update RelatedField.choices to support non-string values --- rest_framework/relations.py | 2 +- tests/test_model_serializer.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rest_framework/relations.py b/rest_framework/relations.py index ad74d1f35..4b6b3bea4 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -168,7 +168,7 @@ class RelatedField(Field): return OrderedDict([ ( - six.text_type(self.to_representation(item)), + self.to_representation(item), self.display_value(item) ) for item in queryset diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index a14972f04..01243ff6e 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -614,7 +614,7 @@ class TestRelationalFieldDisplayValue(TestCase): fields = '__all__' serializer = TestSerializer() - expected = OrderedDict([('1', 'Red Color'), ('2', 'Yellow Color'), ('3', 'Green Color')]) + expected = OrderedDict([(1, 'Red Color'), (2, 'Yellow Color'), (3, 'Green Color')]) self.assertEqual(serializer.fields['color'].choices, expected) def test_custom_display_value(self): @@ -630,7 +630,7 @@ class TestRelationalFieldDisplayValue(TestCase): fields = '__all__' serializer = TestSerializer() - expected = OrderedDict([('1', 'My Red Color'), ('2', 'My Yellow Color'), ('3', 'My Green Color')]) + expected = OrderedDict([(1, 'My Red Color'), (2, 'My Yellow Color'), (3, 'My Green Color')]) self.assertEqual(serializer.fields['color'].choices, expected) From f16e8801679c81b60696d10428e172f0800f4bea Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 10 Aug 2016 17:22:19 +0100 Subject: [PATCH 076/175] Stricter type validation for CharField. (#4380) Stricter type validation for CharField --- rest_framework/fields.py | 6 ++++++ tests/test_fields.py | 2 ++ 2 files changed, 8 insertions(+) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 3a2f27205..fab79808f 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -672,6 +672,7 @@ class NullBooleanField(Field): class CharField(Field): default_error_messages = { + 'invalid': _('Not a valid string.'), 'blank': _('This field may not be blank.'), 'max_length': _('Ensure this field has no more than {max_length} characters.'), 'min_length': _('Ensure this field has at least {min_length} characters.') @@ -702,6 +703,11 @@ class CharField(Field): return super(CharField, self).run_validation(data) def to_internal_value(self, data): + # We're lenient with allowing basic numerics to be coerced into strings, + # but other types should fail. Eg. unclear if booleans should represent as `true` or `True`, + # and composites such as lists are likely user error. + if isinstance(data, bool) or not isinstance(data, six.string_types + six.integer_types + (float,)): + self.fail('invalid') value = six.text_type(data) return value.strip() if self.trim_whitespace else value diff --git a/tests/test_fields.py b/tests/test_fields.py index 1cbff9909..f1a588c27 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -535,6 +535,8 @@ class TestCharField(FieldValues): 'abc': 'abc' } invalid_inputs = { + (): ['Not a valid string.'], + True: ['Not a valid string.'], '': ['This field may not be blank.'] } outputs = { From 3698d9ea2ef95a6823b1bc98863ba7b49e2b6937 Mon Sep 17 00:00:00 2001 From: Kyle Hornberg Date: Wed, 10 Aug 2016 11:23:10 -0500 Subject: [PATCH 077/175] Update permissions.md (#4381) --- docs/api-guide/permissions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 402875cd5..e0838e94a 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -132,7 +132,7 @@ This permission is suitable if you want to your API to allow read permissions to ## DjangoModelPermissions -This permission class ties into Django's standard `django.contrib.auth` [model permissions][contribauth]. This permission must only be applied to views that has a `.queryset` property set. Authorization will only be granted if the user *is authenticated* and has the *relevant model permissions* assigned. +This permission class ties into Django's standard `django.contrib.auth` [model permissions][contribauth]. This permission must only be applied to views that have a `.queryset` property set. Authorization will only be granted if the user *is authenticated* and has the *relevant model permissions* assigned. * `POST` requests require the user to have the `add` permission on the model. * `PUT` and `PATCH` requests require the user to have the `change` permission on the model. From b50d8950eeeac03843b9da1cb96ba52b5b98c8bc Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 11 Aug 2016 11:27:28 +0100 Subject: [PATCH 078/175] Pass request to schema generation (#4383) Pass request to schema generation --- rest_framework/schemas.py | 63 ++++++++++++++++++++------------------- tests/test_schemas.py | 4 +++ 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py index 688deec88..28f5dc8a1 100644 --- a/rest_framework/schemas.py +++ b/rest_framework/schemas.py @@ -65,44 +65,52 @@ class SchemaGenerator(object): urls = import_module(urlconf) else: urls = urlconf - patterns = urls.urlpatterns + self.patterns = urls.urlpatterns elif patterns is None and urlconf is None: urls = import_module(settings.ROOT_URLCONF) - patterns = urls.urlpatterns + self.patterns = urls.urlpatterns + else: + self.patterns = patterns if url and not url.endswith('/'): url += '/' self.title = title self.url = url - self.endpoints = self.get_api_endpoints(patterns) + self.endpoints = None def get_schema(self, request=None): - if request is None: - endpoints = self.endpoints - else: - # Filter the list of endpoints to only include those that - # the user has permission on. - endpoints = [] - for key, link, callback in self.endpoints: - method = link.action.upper() - view = callback.cls() + if self.endpoints is None: + self.endpoints = self.get_api_endpoints(self.patterns) + + links = [] + for key, path, method, callback in self.endpoints: + view = callback.cls() + for attr, val in getattr(callback, 'initkwargs', {}).items(): + setattr(view, attr, val) + view.args = () + view.kwargs = {} + view.format_kwarg = None + + if request is not None: view.request = clone_request(request, method) - view.format_kwarg = None try: view.check_permissions(view.request) except exceptions.APIException: - pass - else: - endpoints.append((key, link, callback)) + continue + else: + view.request = None - if not endpoints: + link = self.get_link(path, method, callback, view) + links.append((key, link)) + + if not link: return None # Generate the schema content structure, from the endpoints. # ('users', 'list'), Link -> {'users': {'list': Link()}} content = {} - for key, link, callback in endpoints: + for key, link in links: insert_into(content, key, link) # Return the schema document. @@ -122,8 +130,7 @@ class SchemaGenerator(object): if self.should_include_endpoint(path, callback): for method in self.get_allowed_methods(callback): key = self.get_key(path, method, callback) - link = self.get_link(path, method, callback) - endpoint = (key, link, callback) + endpoint = (key, path, method, callback) api_endpoints.append(endpoint) elif isinstance(pattern, RegexURLResolver): @@ -190,14 +197,10 @@ class SchemaGenerator(object): # Methods for generating each individual `Link` instance... - def get_link(self, path, method, callback): + def get_link(self, path, method, callback, view): """ Return a `coreapi.Link` instance for the given endpoint. """ - view = callback.cls() - for attr, val in getattr(callback, 'initkwargs', {}).items(): - setattr(view, attr, val) - fields = self.get_path_fields(path, method, callback, view) fields += self.get_serializer_fields(path, method, callback, view) fields += self.get_pagination_fields(path, method, callback, view) @@ -260,20 +263,18 @@ class SchemaGenerator(object): if method not in ('PUT', 'PATCH', 'POST'): return [] - if not hasattr(view, 'get_serializer_class'): + if not hasattr(view, 'get_serializer'): return [] - fields = [] - - serializer_class = view.get_serializer_class() - serializer = serializer_class() + serializer = view.get_serializer() if isinstance(serializer, serializers.ListSerializer): - return coreapi.Field(name='data', location='body', required=True) + return [coreapi.Field(name='data', location='body', required=True)] if not isinstance(serializer, serializers.Serializer): return [] + fields = [] for field in serializer.fields.values(): if field.read_only: continue diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 6c02c9d23..d8c0f2209 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -43,6 +43,10 @@ class ExampleViewSet(ModelViewSet): def custom_action(self, request, pk): return super(ExampleSerializer, self).retrieve(self, request) + def get_serializer(self, *args, **kwargs): + assert self.request + return super(ExampleViewSet, self).get_serializer(*args, **kwargs) + class ExampleView(APIView): permission_classes = [permissions.IsAuthenticatedOrReadOnly] From 01b498ec5109da22bf1b79d86efaecf45426ad51 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 11 Aug 2016 14:07:40 +0100 Subject: [PATCH 079/175] Fix schema categories for custom list actions (#4386) --- rest_framework/schemas.py | 98 +++++++++++++++++++++++---------------- tests/test_schemas.py | 14 +++++- 2 files changed, 70 insertions(+), 42 deletions(-) diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py index 28f5dc8a1..c3a811bfb 100644 --- a/rest_framework/schemas.py +++ b/rest_framework/schemas.py @@ -30,24 +30,6 @@ def is_api_view(callback): return (cls is not None) and issubclass(cls, APIView) -def insert_into(target, keys, item): - """ - Insert `item` into the nested dictionary `target`. - - For example: - - target = {} - insert_into(target, ('users', 'list'), Link(...)) - insert_into(target, ('users', 'detail'), Link(...)) - assert target == {'users': {'list': Link(...), 'detail': Link(...)}} - """ - for key in keys[:1]: - if key not in target: - target[key] = {} - target = target[key] - target[keys[-1]] = item - - class SchemaGenerator(object): default_mapping = { 'get': 'read', @@ -84,7 +66,7 @@ class SchemaGenerator(object): self.endpoints = self.get_api_endpoints(self.patterns) links = [] - for key, path, method, callback in self.endpoints: + for path, method, category, action, callback in self.endpoints: view = callback.cls() for attr, val in getattr(callback, 'initkwargs', {}).items(): setattr(view, attr, val) @@ -102,16 +84,21 @@ class SchemaGenerator(object): view.request = None link = self.get_link(path, method, callback, view) - links.append((key, link)) + links.append((category, action, link)) - if not link: + if not links: return None - # Generate the schema content structure, from the endpoints. - # ('users', 'list'), Link -> {'users': {'list': Link()}} + # Generate the schema content structure, eg: + # {'users': {'list': Link()}} content = {} - for key, link in links: - insert_into(content, key, link) + for category, action, link in links: + if category is None: + content[action] = link + elif category in content: + content[category][action] = link + else: + content[category] = {action: link} # Return the schema document. return coreapi.Document(title=self.title, content=content, url=self.url) @@ -129,8 +116,8 @@ class SchemaGenerator(object): callback = pattern.callback if self.should_include_endpoint(path, callback): for method in self.get_allowed_methods(callback): - key = self.get_key(path, method, callback) - endpoint = (key, path, method, callback) + action = self.get_action(path, method, callback) + endpoint = (path, method, action, callback) api_endpoints.append(endpoint) elif isinstance(pattern, RegexURLResolver): @@ -140,7 +127,21 @@ class SchemaGenerator(object): ) api_endpoints.extend(nested_endpoints) - return api_endpoints + return self.add_categories(api_endpoints) + + def add_categories(self, api_endpoints): + """ + (path, method, action, callback) -> (path, method, category, action, callback) + """ + # Determine the top level categories for the schema content, + # based on the URLs of the endpoints. Eg `set(['users', 'organisations'])` + paths = [endpoint[0] for endpoint in api_endpoints] + categories = self.get_categories(paths) + + return [ + (path, method, self.get_category(categories, path), action, callback) + for (path, method, action, callback) in api_endpoints + ] def get_path(self, path_regex): """ @@ -177,23 +178,38 @@ class SchemaGenerator(object): callback.cls().allowed_methods if method not in ('OPTIONS', 'HEAD') ] - def get_key(self, path, method, callback): + def get_action(self, path, method, callback): """ - Return a tuple of strings, indicating the identity to use for a - given endpoint. eg. ('users', 'list'). + Return a description action string for the endpoint, eg. 'list'. """ - category = None - for item in path.strip('/').split('/'): - if '{' in item: - break - category = item - actions = getattr(callback, 'actions', self.default_mapping) - action = actions[method.lower()] + return actions[method.lower()] - if category: - return (category, action) - return (action,) + def get_categories(self, paths): + categories = set() + split_paths = set([ + tuple(path.split("{")[0].strip('/').split('/')) + for path in paths + ]) + + while split_paths: + for split_path in list(split_paths): + if len(split_path) == 0: + split_paths.remove(split_path) + elif len(split_path) == 1: + categories.add(split_path[0]) + split_paths.remove(split_path) + elif split_path[0] in categories: + split_paths.remove(split_path) + + return categories + + def get_category(self, categories, path): + path_components = path.split("{")[0].strip('/').split('/') + for path_component in path_components: + if path_component in categories: + return path_component + return None # Methods for generating each individual `Link` instance... diff --git a/tests/test_schemas.py b/tests/test_schemas.py index d8c0f2209..5e588483d 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -5,7 +5,7 @@ from django.test import TestCase, override_settings from rest_framework import filters, pagination, permissions, serializers from rest_framework.compat import coreapi -from rest_framework.decorators import detail_route +from rest_framework.decorators import detail_route, list_route from rest_framework.response import Response from rest_framework.routers import DefaultRouter from rest_framework.schemas import SchemaGenerator @@ -43,6 +43,10 @@ class ExampleViewSet(ModelViewSet): def custom_action(self, request, pk): return super(ExampleSerializer, self).retrieve(self, request) + @list_route() + def custom_list_action(self, request): + return super(ExampleViewSet, self).list(self, request) + def get_serializer(self, *args, **kwargs): assert self.request return super(ExampleViewSet, self).get_serializer(*args, **kwargs) @@ -88,6 +92,10 @@ class TestRouterGeneratedSchema(TestCase): coreapi.Field('ordering', required=False, location='query') ] ), + 'custom_list_action': coreapi.Link( + url='/example/custom_list_action/', + action='get' + ), 'retrieve': coreapi.Link( url='/example/{pk}/', action='get', @@ -144,6 +152,10 @@ class TestRouterGeneratedSchema(TestCase): coreapi.Field('d', required=False, location='form'), ] ), + 'custom_list_action': coreapi.Link( + url='/example/custom_list_action/', + action='get' + ), 'update': coreapi.Link( url='/example/{pk}/', action='put', From 116917dbed76ab37ab2d07f7b938d13f2c6e3efd Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 11 Aug 2016 16:18:33 +0100 Subject: [PATCH 080/175] Add form field descriptions to schemas (#4387) --- rest_framework/schemas.py | 9 ++++++++- tests/test_schemas.py | 8 ++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py index c3a811bfb..6b6324033 100644 --- a/rest_framework/schemas.py +++ b/rest_framework/schemas.py @@ -4,6 +4,7 @@ from django.conf import settings from django.contrib.admindocs.views import simplify_regex from django.core.urlresolvers import RegexURLPattern, RegexURLResolver from django.utils import six +from django.utils.encoding import force_text from rest_framework import exceptions, serializers from rest_framework.compat import coreapi, uritemplate, urlparse @@ -295,7 +296,13 @@ class SchemaGenerator(object): if field.read_only: continue required = field.required and method != 'PATCH' - field = coreapi.Field(name=field.source, location='form', required=required) + description = force_text(field.help_text) if field.help_text else '' + field = coreapi.Field( + name=field.source, + location='form', + required=required, + description=description + ) fields.append(field) return fields diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 5e588483d..81b796c35 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -24,7 +24,7 @@ class ExamplePagination(pagination.PageNumberPagination): class ExampleSerializer(serializers.Serializer): - a = serializers.CharField(required=True) + a = serializers.CharField(required=True, help_text='A field description') b = serializers.CharField(required=False) @@ -131,7 +131,7 @@ class TestRouterGeneratedSchema(TestCase): action='post', encoding='application/json', fields=[ - coreapi.Field('a', required=True, location='form'), + coreapi.Field('a', required=True, location='form', description='A field description'), coreapi.Field('b', required=False, location='form') ] ), @@ -162,7 +162,7 @@ class TestRouterGeneratedSchema(TestCase): encoding='application/json', fields=[ coreapi.Field('pk', required=True, location='path'), - coreapi.Field('a', required=True, location='form'), + coreapi.Field('a', required=True, location='form', description='A field description'), coreapi.Field('b', required=False, location='form') ] ), @@ -172,7 +172,7 @@ class TestRouterGeneratedSchema(TestCase): encoding='application/json', fields=[ coreapi.Field('pk', required=True, location='path'), - coreapi.Field('a', required=False, location='form'), + coreapi.Field('a', required=False, location='form', description='A field description'), coreapi.Field('b', required=False, location='form') ] ), From 1312acaf8b0ef2cdea1f6b296196899e890c8064 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 11 Aug 2016 16:53:34 +0100 Subject: [PATCH 081/175] Minor docs update [ci skip] --- README.md | 3 +-- docs/index.md | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a8e1afbf1..179f2891a 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,7 @@ REST framework commercially we strongly encourage you to invest in its continued development by **[signing up for a paid plan][funding]**. The initial aim is to provide a single full-time position on REST framework. -Right now we're over 58% of the way towards achieving that. -*Every single sign-up makes a significant impact.* +*Every single sign-up makes a significant impact towards making that possible.*

diff --git a/docs/index.md b/docs/index.md index 87e013b8e..88276e678 100644 --- a/docs/index.md +++ b/docs/index.md @@ -68,8 +68,7 @@ REST framework commercially we strongly encourage you to invest in its continued development by **[signing up for a paid plan][funding]**. The initial aim is to provide a single full-time position on REST framework. -Right now we're over 58% of the way towards achieving that. -*Every single sign-up makes a significant impact.* +*Every single sign-up makes a significant impact towards making that possible.*

-*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), and [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf).* +*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), and [Machinalis](http://www.machinalis.com/#services).* --- From b683cd7afc9e03ac5287172348346ff2e61cc348 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 25 Aug 2016 22:29:38 +0100 Subject: [PATCH 104/175] Update sponsor info [ci skip] --- docs/img/premium/stream-readme.png | Bin 19341 -> 20667 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/img/premium/stream-readme.png b/docs/img/premium/stream-readme.png index a04009d7fc0d1fe4fab5aa77215a94d5150d6354..955c11429c53bd18cbfbf888dca7663e134264eb 100644 GIT binary patch literal 20667 zcmeEuWmKHYvM4gRyTf21!3THOU;IwTEQx#a&!GBG%Fvw{{`(&&;N>MY4P7uxp=xc{3Vm61(%hBm7|rD zhdUe(^dHH=b^G6l{}+=!W-fmd`%8&`%O>`Ra}iA|cV`FBKPu36vh$GO5&KJ^e{cRD zjr@%z>+I;_X65b( z4zKs02>i409|(U`FY?OH$I3xh&JM27zog*f6%yn6k3Ij6DeLUu?55>nW?}V*v%gUO z(e&@of6>wVA38jI!vCc6kDh;ET3U#>dzyo-EIj@(U+`J`%lc)^Y`Ff>EXMU8p~c{~ zAfo1MX=m*#XXarg!NUy_5aAXQ;o%4J@cen;77*k5H<*8L@TW)C&C1Nf+3k(9vxCH6 zVmSVJ1@dyjXZ+v$|Ai{X^~aC?;a7h#M*&OB^j+-z9qH3J@Qu3WiV zG%%h+wdWXqUnZwa%p6Os9D}I}#J~qiF)4DAS-FDZHGJJfbz;_)V+bN8uSjc|LpW~N}iWVTtNTH)6 z(gOiQ+j;cV;k!QZ1nl0u?6=!j?t{(z!~@+e64yzNUZt;9n7Gn$kH5<`7}s)yYF zC;UgX|6kSr?;02SC)c$ZPkQckg6MGYS57c#(yXpprXK5*wc2Os9O9a<=PUI0FW~0P zlP%=wzs`ssH$C5SMh{!>N~_2rsCzZwu#{XYUMFltg-SE~X8 zvGCUQw)R_~pSGf=dwx0}g9BbwSxxh<0tYJ6d$=c0)W99rk5r@X?KXP2y?tJO#9=Di zX1&kLZ_<|57V{EqVoq!};WP|i-RAiM=qC#QKohI;za2#nKW(?W`>hYr&#uU!sLROc zTAc41B!B_<8g7YX6=ePzoEIG+sR>nuf#uQPpdf7(pLNSXW3*kHv7Yt4N|vhwEA&)8 zJNWuy(g8MR$+4tQ;$jDp$Bb?$b7fG!GkypCo`$Ng&wz|zT=X_=ISm6tb_I9oPgm?skvZJk0MWBb>$gv4=O{DJLQ}J)<+aUa3 zvctawzUPxZ8_C(vUL-)8iILiS!YU_XNzg~XVR)6)aR4JIg(4`~=?2a{i6-Npm{8Z? zam6gG3k{53gGHXhwF*M_cnCHJRUA;$zv@KqAqWW^AARlb;yXea1-$!_$%C}Gh^(^d zwZ=ifqNOaDIW-<3HNyZjFJJsT*U=n?FdF4O;&ctp68W+l@U0IIZ=wI=i@pj`_rf(y zM1ss?MEX|@NZ1|5kqCzx9TmaHH(f=uu{{h;If((|77=nFr-+G3Z<#!I_{y4XQN$bG z;Rzsj?z>OQl|g`}AR*4*OA}%w;bjfZ52`F2v`e}($64svA2WUUXue!2TOufvX9>q- zmMx)s!v-gl^VSlJeu4nLZ77OeDx+`FOUc+p&j>Zqh+=i@JT7oT7Vzc- zMT_muDVx1a`>W$+ot$aLJm$kLa!&dhE7D2o{4S-pcI1_EBj7tigm>G;_#6TKD{XX$ zlCF+p?`3pgSt`Pz0A!Kg)*`8O)xqAoRJ7Ob;gN6P3q_Hhvta&vHAqu3$jPL&8TWI8 z{?V29Pxb1b>h~HVt@Y~oNFU-u`08Kg7$sCJp-&*YO>TXAIBPy|ApbS;E?mz2T(exf zz6}Lt^J18EP>kzDeIaSKZr<#$JDEJp5~&NJ%}*zLg$Dy8b&@3pzCw4R1D-T+9uwLK zkhDWX4iIli@P|e~g0T^IIFB+~- zWYt`2dd}{BUnoN)GelSci*V!KgR>}sKH+LHo2C!@MRPSVUm#(#EabsIX|3V-axwL0 z6Q&s0J1lj8( zS43PV%AtEihD2BJhF`YU;3rTLf9Ludvk;~

pjlIcQ4o>(`XbcjU>*qQ2?ZI)oLo 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 literal 19341 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 dQepYy|C1zl-M+ID`#}d}dAj Date: Tue, 30 Aug 2016 11:37:48 +0200 Subject: [PATCH 105/175] [documentation] Throttling random request code example fix (#4451) --- docs/api-guide/throttling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index 51d2beef1..da4d5f725 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -188,7 +188,7 @@ The following is an example of a rate throttle, that will randomly throttle 1 in class RandomRateThrottle(throttling.BaseThrottle): def allow_request(self, request, view): - return random.randint(1, 10) == 1 + return random.randint(1, 10) != 1 [cite]: https://dev.twitter.com/docs/error-codes-responses [permissions]: permissions.md From 07efbdb45e5c7ef70249e985b3c1ef1cead455ba Mon Sep 17 00:00:00 2001 From: Mathieu Pillard Date: Fri, 2 Sep 2016 18:00:03 +0200 Subject: [PATCH 106/175] Fix APIClient.get() when path contains unicode arguments (#4458) --- rest_framework/test.py | 9 ++++++--- tests/test_testing.py | 7 +++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/rest_framework/test.py b/rest_framework/test.py index 3ba4059a9..fd9f6ab13 100644 --- a/rest_framework/test.py +++ b/rest_framework/test.py @@ -79,10 +79,13 @@ class APIRequestFactory(DjangoRequestFactory): r = { 'QUERY_STRING': urlencode(data or {}, doseq=True), } - # Fix to support old behavior where you have the arguments in the url - # See #1461 if not data and '?' in path: - r['QUERY_STRING'] = path.split('?')[1] + # Fix to support old behavior where you have the arguments in the + # url. See #1461. + query_string = force_bytes(path.split('?')[1]) + if six.PY3: + query_string = query_string.decode('iso-8859-1') + r['QUERY_STRING'] = query_string r.update(extra) return self.generic('GET', path, **r) diff --git a/tests/test_testing.py b/tests/test_testing.py index 3adcc55f8..6683ae6ed 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -245,3 +245,10 @@ class TestAPIRequestFactory(TestCase): self.assertEqual(dict(request.GET), {'demo': ['test']}) request = factory.get('/view/', {'demo': 'test'}) self.assertEqual(dict(request.GET), {'demo': ['test']}) + + def test_request_factory_url_arguments_with_unicode(self): + factory = APIRequestFactory() + request = factory.get('/view/?demo=testé') + self.assertEqual(dict(request.GET), {'demo': ['testé']}) + request = factory.get('/view/', {'demo': 'testé'}) + self.assertEqual(dict(request.GET), {'demo': ['testé']}) From 5df54a711f3770de28c0aaed9350e589df082117 Mon Sep 17 00:00:00 2001 From: TakesxiSximada Date: Mon, 5 Sep 2016 19:16:41 +0900 Subject: [PATCH 107/175] Set a view function's __module__ value to the WrappedAPIView object's __module__ (#4465) --- rest_framework/decorators.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py index 1b21e643b..554e5236c 100644 --- a/rest_framework/decorators.py +++ b/rest_framework/decorators.py @@ -55,6 +55,7 @@ def api_view(http_method_names=None): setattr(WrappedAPIView, method.lower(), handler) WrappedAPIView.__name__ = func.__name__ + WrappedAPIView.__module__ = func.__module__ WrappedAPIView.renderer_classes = getattr(func, 'renderer_classes', APIView.renderer_classes) From 6b6f3195092cb7f12cddf0dee2bfa4a0eaf3b15c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Padilla?= Date: Thu, 8 Sep 2016 09:01:26 -0400 Subject: [PATCH 108/175] Add missing comma (#4473) --- rest_framework/templates/rest_framework/admin.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/templates/rest_framework/admin.html b/rest_framework/templates/rest_framework/admin.html index eb2b8f1c7..de011cd09 100644 --- a/rest_framework/templates/rest_framework/admin.html +++ b/rest_framework/templates/rest_framework/admin.html @@ -232,7 +232,7 @@ {% block script %} From e91ffc87cb1e5b1148e839a0c3536de4da8efeef Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Tue, 13 Sep 2016 07:21:10 +0200 Subject: [PATCH 109/175] Ignore empty args in the `MultipleFieldLookupMixin` definition - Closes #4484 --- docs/api-guide/generic-views.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index d7dc30ce1..5fea8d7e0 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -330,7 +330,8 @@ For example, if you need to lookup objects based on multiple fields in the URL c queryset = self.filter_queryset(queryset) # Apply any filter backends filter = {} for field in self.lookup_fields: - filter[field] = self.kwargs[field] + if self.kwargs[field]: # Ignore empty fields. + filter[field] = self.kwargs[field] return get_object_or_404(queryset, **filter) # Lookup the object You can then simply apply this mixin to a view or viewset anytime you need to apply the custom behavior. From a68b37d8bc432fae37ef5880aec500002b59f565 Mon Sep 17 00:00:00 2001 From: Jeff Willette Date: Tue, 13 Sep 2016 17:31:48 +0900 Subject: [PATCH 110/175] Update to correct location of reverse relation doc (#4481) --- docs/api-guide/serializers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 70fab448c..5772d940a 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -442,7 +442,7 @@ Declaring a `ModelSerializer` looks like this: By default, all the model fields on the class will be mapped to a corresponding serializer fields. -Any relationships such as foreign keys on the model will be mapped to `PrimaryKeyRelatedField`. Reverse relationships are not included by default unless explicitly included as described below. +Any relationships such as foreign keys on the model will be mapped to `PrimaryKeyRelatedField`. Reverse relationships are not included by default unless explicitly included as specified in the [serializer relations][relations] documentation. #### Inspecting a `ModelSerializer` From 4655501d51080fbc7733a81fab1a227229a33e3f Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 15 Sep 2016 12:44:45 +0100 Subject: [PATCH 111/175] Fix regression of `RegexField`. (#4490) * Don't deepcopy 'regex' arguments, instead treat as immutable. --- rest_framework/fields.py | 21 ++++++++++++--------- tests/test_fields.py | 15 +++++++++++++++ tests/test_serializer.py | 14 ++++++++++++++ 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index f76e4e801..917a151e5 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -252,6 +252,8 @@ class SkipField(Exception): pass +REGEX_TYPE = type(re.compile('')) + NOT_READ_ONLY_WRITE_ONLY = 'May not set both `read_only` and `write_only`' NOT_READ_ONLY_REQUIRED = 'May not set both `read_only` and `required`' NOT_REQUIRED_DEFAULT = 'May not set both `required` and `default`' @@ -581,16 +583,17 @@ class Field(object): When cloning fields we instantiate using the arguments it was originally created with, rather than copying the complete state. """ - args = copy.deepcopy(self._args) - kwargs = dict(self._kwargs) - # Bit ugly, but we need to special case 'validators' as Django's - # RegexValidator does not support deepcopy. - # We treat validator callables as immutable objects. + # Treat regexes and validators as immutable. # See https://github.com/tomchristie/django-rest-framework/issues/1954 - validators = kwargs.pop('validators', None) - kwargs = copy.deepcopy(kwargs) - if validators is not None: - kwargs['validators'] = validators + # and https://github.com/tomchristie/django-rest-framework/pull/4489 + args = [ + copy.deepcopy(item) if not isinstance(item, REGEX_TYPE) else item + for item in self._args + ] + kwargs = { + key: (copy.deepcopy(value) if (key not in ('validators', 'regex')) else value) + for key, value in self._kwargs.items() + } return self.__class__(*args, **kwargs) def __repr__(self): diff --git a/tests/test_fields.py b/tests/test_fields.py index f1a588c27..4a4b741c5 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1,5 +1,6 @@ import datetime import os +import re import uuid from decimal import Decimal @@ -590,6 +591,20 @@ class TestRegexField(FieldValues): field = serializers.RegexField(regex='[a-z][0-9]') +class TestiCompiledRegexField(FieldValues): + """ + Valid and invalid values for `RegexField`. + """ + valid_inputs = { + 'a9': 'a9', + } + invalid_inputs = { + 'A9': ["This value does not match the required pattern."] + } + outputs = {} + field = serializers.RegexField(regex=re.compile('[a-z][0-9]')) + + class TestSlugField(FieldValues): """ Valid and invalid values for `SlugField`. diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 4e9080909..bd9ef9500 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals import pickle +import re import pytest @@ -337,3 +338,16 @@ class TestDefaultInclusions: assert serializer.is_valid() assert serializer.validated_data == {'integer': 456} assert serializer.errors == {} + + +class TestSerializerValidationWithCompiledRegexField: + def setup(self): + class ExampleSerializer(serializers.Serializer): + name = serializers.RegexField(re.compile(r'\d'), required=True) + self.Serializer = ExampleSerializer + + def test_validation_success(self): + serializer = self.Serializer(data={'name': '2'}) + assert serializer.is_valid() + assert serializer.validated_data == {'name': '2'} + assert serializer.errors == {} From fe4c4fa75147f114e014c3d6623d84de35c01fe7 Mon Sep 17 00:00:00 2001 From: Tanner Hobson Date: Fri, 16 Sep 2016 22:09:49 -0400 Subject: [PATCH 112/175] Fix indentation regression in API listing (#4493) In commit 5392be4ddba37da3e50c3feabc8b3b1c944481a8, there was a change made when cleaning up the template for the API listing that caused 2 spaces to appear before every header item (except the first) and before the first line of the body of the response. This meant that it often looked like: HTTP 200 OK Allow: GET, OPTIONS Content-Type: application/json Vary: Accept { "key": "value", "key2": "value2" } This change removes those leading spaces, so that it will now look like: HTTP 200 OK Allow: GET, OPTIONS Content-Type: application/json Vary: Accept { "key": "value", "key2": "value2" } --- rest_framework/templates/rest_framework/base.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html index 7f5170a62..5df23b767 100644 --- a/rest_framework/templates/rest_framework/base.html +++ b/rest_framework/templates/rest_framework/base.html @@ -150,10 +150,10 @@

-
HTTP {{ response.status_code }} {{ response.status_text }}{% autoescape off %}
-  {% for key, val in response_headers.items %}{{ key }}: {{ val|break_long_headers|urlize_quoted_links }}
-  {% endfor %}
-  {{ content|urlize_quoted_links }}
{% endautoescape %} +
HTTP {{ response.status_code }} {{ response.status_text }}{% autoescape off %}{% for key, val in response_headers.items %}
+{{ key }}: {{ val|break_long_headers|urlize_quoted_links }}{% endfor %}
+
+{{ content|urlize_quoted_links }}
{% endautoescape %}
From fe96ceced08fa36db122deeb53e525e25e3f3daf Mon Sep 17 00:00:00 2001 From: Ollie Ford Date: Sat, 17 Sep 2016 03:13:34 +0100 Subject: [PATCH 113/175] fixes response rendering with empty context (#4495) This commit allows `response.render` to be called when `response.rendered_context == {}`. This should be allowed, since if [the JSONRenderer, for example](https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/renderers.py#L85-L92) receives a `None` context, it sets it to an empty dictionary itself. --- rest_framework/response.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/response.py b/rest_framework/response.py index 4b863cb99..cb0f290ce 100644 --- a/rest_framework/response.py +++ b/rest_framework/response.py @@ -56,7 +56,7 @@ class Response(SimpleTemplateResponse): assert renderer, ".accepted_renderer not set on Response" assert accepted_media_type, ".accepted_media_type not set on Response" - assert context, ".renderer_context not set on Response" + assert context is not None, ".renderer_context not set on Response" context['response'] = self media_type = renderer.media_type From 76cc2f031940c2440f27c0070ee709f7f1304fe5 Mon Sep 17 00:00:00 2001 From: Jozef Knaperek Date: Mon, 19 Sep 2016 21:52:06 +0200 Subject: [PATCH 114/175] Rename an invalid reference to BasicToken in the docs --- docs/api-guide/authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 8d880b037..bf3a31eb7 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -148,7 +148,7 @@ For clients to authenticate, the token key should be included in the `Authorizat If successfully authenticated, `TokenAuthentication` provides the following credentials. * `request.user` will be a Django `User` instance. -* `request.auth` will be a `rest_framework.authtoken.models.BasicToken` instance. +* `request.auth` will be a `rest_framework.authtoken.models.Token` instance. Unauthenticated responses that are denied permission will result in an `HTTP 401 Unauthorized` response with an appropriate WWW-Authenticate header. For example: From be74d11165c364687ecc41bc6eb0970af24670fa Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 21 Sep 2016 11:49:09 +0100 Subject: [PATCH 115/175] Fallback behavior for request parsing when request.POST already accessed. (#4500) --- rest_framework/request.py | 27 +++++++++++++++++++++--- tests/test_parsers.py | 43 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/rest_framework/request.py b/rest_framework/request.py index 355cccad7..0a827728a 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -15,6 +15,7 @@ import sys from django.conf import settings from django.http import QueryDict from django.http.multipartparser import parse_header +from django.http.request import RawPostDataException from django.utils import six from django.utils.datastructures import MultiValueDict @@ -263,10 +264,20 @@ class Request(object): if content_length == 0: self._stream = None - elif hasattr(self._request, 'read'): + elif not self._request._read_started: self._stream = self._request else: - self._stream = six.BytesIO(self.raw_post_data) + self._stream = six.BytesIO(self.body) + + def _supports_form_parsing(self): + """ + Return True if this requests supports parsing form data. + """ + form_media = ( + 'application/x-www-form-urlencoded', + 'multipart/form-data' + ) + return any([parser.media_type in form_media for parser in self.parsers]) def _parse(self): """ @@ -274,8 +285,18 @@ class Request(object): May raise an `UnsupportedMediaType`, or `ParseError` exception. """ - stream = self.stream media_type = self.content_type + try: + stream = self.stream + except RawPostDataException: + if not hasattr(self._request, '_post'): + raise + # If request.POST has been accessed in middleware, and a method='POST' + # request was made with 'multipart/form-data', then the request stream + # will already have been exhausted. + if self._supports_form_parsing(): + return (self._request.POST, self._request.FILES) + stream = None if stream is None or media_type is None: empty_data = QueryDict('', encoding=self._request._encoding) diff --git a/tests/test_parsers.py b/tests/test_parsers.py index f3af6817f..5052e2e53 100644 --- a/tests/test_parsers.py +++ b/tests/test_parsers.py @@ -7,11 +7,16 @@ from django import forms from django.core.files.uploadhandler import ( MemoryFileUploadHandler, TemporaryFileUploadHandler ) +from django.http.request import RawPostDataException from django.test import TestCase from django.utils.six.moves import StringIO from rest_framework.exceptions import ParseError -from rest_framework.parsers import FileUploadParser, FormParser +from rest_framework.parsers import ( + FileUploadParser, FormParser, JSONParser, MultiPartParser +) +from rest_framework.request import Request +from rest_framework.test import APIRequestFactory class Form(forms.Form): @@ -122,3 +127,39 @@ class TestFileUploadParser(TestCase): def __replace_content_disposition(self, disposition): self.parser_context['request'].META['HTTP_CONTENT_DISPOSITION'] = disposition + + +class TestPOSTAccessed(TestCase): + def setUp(self): + self.factory = APIRequestFactory() + + def test_post_accessed_in_post_method(self): + django_request = self.factory.post('/', {'foo': 'bar'}) + request = Request(django_request, parsers=[FormParser(), MultiPartParser()]) + django_request.POST + assert request.POST == {'foo': ['bar']} + assert request.data == {'foo': ['bar']} + + def test_post_accessed_in_post_method_with_json_parser(self): + django_request = self.factory.post('/', {'foo': 'bar'}) + request = Request(django_request, parsers=[JSONParser()]) + django_request.POST + assert request.POST == {} + assert request.data == {} + + def test_post_accessed_in_put_method(self): + django_request = self.factory.put('/', {'foo': 'bar'}) + request = Request(django_request, parsers=[FormParser(), MultiPartParser()]) + django_request.POST + assert request.POST == {'foo': ['bar']} + assert request.data == {'foo': ['bar']} + + def test_request_read_before_parsing(self): + django_request = self.factory.put('/', {'foo': 'bar'}) + request = Request(django_request, parsers=[FormParser(), MultiPartParser()]) + django_request.read() + with pytest.raises(RawPostDataException): + request.POST + with pytest.raises(RawPostDataException): + request.POST + request.data From 7ab4a587d981f45ca440c53fa5514ad57e80abc3 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 21 Sep 2016 12:24:26 +0100 Subject: [PATCH 116/175] Version 3.4.7 (#4501) --- docs/topics/release-notes.md | 28 ++++++++++++++++++++++++++++ rest_framework/__init__.py | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 63aa4e4a3..446abdd14 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,6 +40,18 @@ You can determine your currently installed version using `pip freeze`: ## 3.4.x series +### 3.4.7 + +**Date**: [21st September 2016][3.4.7-milestone] + +* Fallback behavior for request parsing when request.POST already accessed. ([#3951][gh3951], [#4500][gh4500]) +* Fix regression of `RegexField`. ([#4489][gh4489], [#4490][gh4490], [#2617][gh2617]) +* Missing comma in `admin.html` causing CSRF error. ([#4472][gh4472], [#4473][gh4473]) +* Fix response rendering with empty context. ([#4495][gh4495]) +* Fix indentation regression in API listing. ([#4493][gh4493]) +* Fixed an issue where the incorrect value is set to `ResolverMatch.func_name` of api_view decorated view. ([#4465][gh4465], [#4462][gh4462]) +* Fix `APIClient.get()` when path contains unicode arguments ([#4458][gh4458]) + ### 3.4.6 **Date**: [23rd August 2016][3.4.6-milestone] @@ -583,6 +595,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.4.4-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.4+Release%22 [3.4.5-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.5+Release%22 [3.4.6-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.6+Release%22 +[3.4.7-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.7+Release%22 [gh2013]: https://github.com/tomchristie/django-rest-framework/issues/2013 @@ -1109,3 +1122,18 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh3508]: https://github.com/tomchristie/django-rest-framework/issues/3508 [gh4419]: https://github.com/tomchristie/django-rest-framework/issues/4419 [gh4423]: https://github.com/tomchristie/django-rest-framework/issues/4423 + + + +[gh3951]: https://github.com/tomchristie/django-rest-framework/issues/3951 +[gh4500]: https://github.com/tomchristie/django-rest-framework/issues/4500 +[gh4489]: https://github.com/tomchristie/django-rest-framework/issues/4489 +[gh4490]: https://github.com/tomchristie/django-rest-framework/issues/4490 +[gh2617]: https://github.com/tomchristie/django-rest-framework/issues/2617 +[gh4472]: https://github.com/tomchristie/django-rest-framework/issues/4472 +[gh4473]: https://github.com/tomchristie/django-rest-framework/issues/4473 +[gh4495]: https://github.com/tomchristie/django-rest-framework/issues/4495 +[gh4493]: https://github.com/tomchristie/django-rest-framework/issues/4493 +[gh4465]: https://github.com/tomchristie/django-rest-framework/issues/4465 +[gh4462]: https://github.com/tomchristie/django-rest-framework/issues/4462 +[gh4458]: https://github.com/tomchristie/django-rest-framework/issues/4458 diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index d99d82d23..457af6c88 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ ______ _____ _____ _____ __ """ __title__ = 'Django REST framework' -__version__ = '3.4.6' +__version__ = '3.4.7' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2016 Tom Christie' From b82ec540ad447dcf703a9b21e9e94976109fd175 Mon Sep 17 00:00:00 2001 From: Dmitry Dygalo Date: Sat, 24 Sep 2016 00:03:02 +0200 Subject: [PATCH 117/175] Remove code for old Django versions (#4513) --- rest_framework/views.py | 1 - rest_framework/viewsets.py | 4 ---- 2 files changed, 5 deletions(-) diff --git a/rest_framework/views.py b/rest_framework/views.py index 15d8c6cde..e178a209f 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -126,7 +126,6 @@ class APIView(View): 'Use `.all()` or call `.get_queryset()` instead.' ) cls.queryset._fetch_all = force_evaluation - cls.queryset._result_iter = force_evaluation # Django <= 1.5 view = super(APIView, cls).as_view(**initkwargs) view.cls = cls diff --git a/rest_framework/viewsets.py b/rest_framework/viewsets.py index bd8333504..a9dc632ff 100644 --- a/rest_framework/viewsets.py +++ b/rest_framework/viewsets.py @@ -79,10 +79,6 @@ class ViewSetMixin(object): handler = getattr(self, action) setattr(self, method, handler) - # Patch this in as it's otherwise only present from 1.5 onwards - if hasattr(self, 'get') and not hasattr(self, 'head'): - self.head = self.get - # And continue as usual return self.dispatch(request, *args, **kwargs) From 38aa0ac28131f6c32cc3359efcf734012fb435d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Padilla?= Date: Wed, 28 Sep 2016 00:05:22 -0400 Subject: [PATCH 118/175] Update Django security releases 1.9.10 and 1.8.15 --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 1e8a7e5c4..8c20faa2b 100644 --- a/tox.ini +++ b/tox.ini @@ -15,8 +15,8 @@ setenv = PYTHONDONTWRITEBYTECODE=1 PYTHONWARNINGS=once deps = - django18: Django==1.8.14 - django19: Django==1.9.9 + django18: Django==1.8.15 + django19: Django==1.9.10 django110: Django==1.10 djangomaster: https://github.com/django/django/archive/master.tar.gz -rrequirements/requirements-testing.txt From e7fd166048de8017c11152b102a80602a80d20c1 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 29 Sep 2016 21:29:21 +0100 Subject: [PATCH 119/175] Docs tweaks --- docs/api-guide/relations.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md index 8695b2c1e..42533823c 100644 --- a/docs/api-guide/relations.md +++ b/docs/api-guide/relations.md @@ -286,7 +286,7 @@ Would serialize to a nested representation like this: ], } -# Writable nested serializers +## Writable nested serializers By default nested serializers are read-only. If you want to support write-operations to a nested serializer field you'll need to create `create()` and/or `update()` methods in order to explicitly specify how the child relationships should be saved. @@ -324,8 +324,14 @@ By default nested serializers are read-only. If you want to support write-operat >>> serializer.save() +--- + # Custom relational fields +In rare cases where none of the existing relational styles fit the representation you need, +you can implement a completely custom relational field, that describes exactly how the +output representation should be generated from the model instance. + To implement a custom relational field, you should override `RelatedField`, and implement the `.to_representation(self, value)` method. This method takes the target of the field as the `value` argument, and should return the representation that should be used to serialize the target. The `value` argument will typically be a model instance. If you want to implement a read-write relational field, you must also implement the `.to_internal_value(self, data)` method. From 73fd1ff4e71bec2cb70e8e0bd659712af98fa6d4 Mon Sep 17 00:00:00 2001 From: Manjit Kumar Date: Fri, 30 Sep 2016 16:32:23 +0530 Subject: [PATCH 120/175] add drf-url-filters app to django-rest-framework in filtering docs (#4528) - enabling validtions on incoming query params with the ease of adding new filters as easy as adding a new key in a dict. --- docs/api-guide/filtering.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index 0ccd51dd3..bc502e970 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -432,6 +432,10 @@ The [djangorestframework-word-filter][django-rest-framework-word-search-filter] [django-url-filter][django-url-filter] provides a safe way to filter data via human-friendly URLs. It works very similar to DRF serializers and fields in a sense that they can be nested except they are called filtersets and filters. That provides easy way to filter related data. Also this library is generic-purpose so it can be used to filter other sources of data and not only Django `QuerySet`s. +## drf-url-filters + +[drf-url-filter][drf-url-filter] is a simple Django app to apply filters on drf `ModelViewSet`'s `Queryset` in a clean, simple and configurable way. It also supports validations on incoming query params and their values. A beautiful python package `Voluptouos` is being used for validations on the incoming query parameters. The best part about voluptouos is you can define your own validations as per your query params requirements. + [cite]: https://docs.djangoproject.com/en/dev/topics/db/queries/#retrieving-specific-objects-with-filters [django-filter]: https://github.com/alex/django-filter [django-filter-docs]: https://django-filter.readthedocs.io/en/latest/index.html @@ -443,3 +447,5 @@ The [djangorestframework-word-filter][django-rest-framework-word-search-filter] [django-rest-framework-filters]: https://github.com/philipn/django-rest-framework-filters [django-rest-framework-word-search-filter]: https://github.com/trollknurr/django-rest-framework-word-search-filter [django-url-filter]: https://github.com/miki725/django-url-filter +[drf-url-filter]: https://github.com/manjitkumar/drf-url-filters + From 1b882f7281b475a8cb6eb993299d3ec2248dc535 Mon Sep 17 00:00:00 2001 From: Danilo Bargen Date: Fri, 30 Sep 2016 18:06:36 +0200 Subject: [PATCH 121/175] Add drf-dynamic-fields to third party packages (#4530) --- docs/api-guide/serializers.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 5772d940a..290e32f4f 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -1089,6 +1089,7 @@ The following third party packages are also available. The [django-rest-marshmallow][django-rest-marshmallow] package provides an alternative implementation for serializers, using the python [marshmallow][marshmallow] library. It exposes the same API as the REST framework serializers, and can be used as a drop-in replacement in some use-cases. ## Serpy + The [serpy][serpy] package is an alternative implementation for serializers that is built for speed. [Serpy][serpy] serializes complex datatypes to simple native types. The native types can be easily converted to JSON or any other format needed. ## MongoengineModelSerializer @@ -1107,7 +1108,12 @@ The [django-rest-framework-hstore][django-rest-framework-hstore] package provide The [dynamic-rest][dynamic-rest] package extends the ModelSerializer and ModelViewSet interfaces, adding API query parameters for filtering, sorting, and including / excluding all fields and relationships defined by your serializers. +## Dynamic Fields Mixin + +The [drf-dynamic-fields][drf-dynamic-fields] package provides a mixin to dynamically limit the fields per serializer to a subset specified by an URL parameter. + ## HTML JSON Forms + The [html-json-forms][html-json-forms] package provides an algorithm and serializer for processing `
` submissions per the (inactive) [HTML JSON Form specification][json-form-spec]. The serializer facilitates processing of arbitrarily nested JSON structures within HTML. For example, `` will be interpreted as `{"items": [{"id": "5"}]}`. [cite]: https://groups.google.com/d/topic/django-users/sVFaOfQi4wY/discussion @@ -1124,3 +1130,4 @@ The [html-json-forms][html-json-forms] package provides an algorithm and seriali [dynamic-rest]: https://github.com/AltSchool/dynamic-rest [html-json-forms]: https://github.com/wq/html-json-forms [json-form-spec]: https://www.w3.org/TR/html-json-forms/ +[drf-dynamic-fields]: https://github.com/dbrgn/drf-dynamic-fields From b8fcb7807a11f37c88e9bfc6eaf999875e392114 Mon Sep 17 00:00:00 2001 From: Angel Velasquez Date: Sun, 2 Oct 2016 03:11:40 -0300 Subject: [PATCH 122/175] Update Django security release 1.10.2 --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 8c20faa2b..48cecccf7 100644 --- a/tox.ini +++ b/tox.ini @@ -17,7 +17,7 @@ setenv = deps = django18: Django==1.8.15 django19: Django==1.9.10 - django110: Django==1.10 + django110: Django==1.10.2 djangomaster: https://github.com/django/django/archive/master.tar.gz -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt From 883efbc19fb124db7741caad512afb5b482e291d Mon Sep 17 00:00:00 2001 From: Rowan Seymour Date: Tue, 4 Oct 2016 14:44:50 +0200 Subject: [PATCH 123/175] Case insensitive uniqueness validation (#4534) --- docs/api-guide/validators.md | 1 + rest_framework/validators.py | 5 +++-- tests/test_validators.py | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/api-guide/validators.md b/docs/api-guide/validators.md index f04c74c3c..9df15ec15 100644 --- a/docs/api-guide/validators.md +++ b/docs/api-guide/validators.md @@ -61,6 +61,7 @@ It takes a single required argument, and an optional `messages` argument: * `queryset` *required* - This is the queryset against which uniqueness should be enforced. * `message` - The error message that should be used when validation fails. +* `lookup` - The lookup used to find an existing instance with the value being validated. Defaults to `'exact'`. This validator should be applied to *serializer fields*, like so: diff --git a/rest_framework/validators.py b/rest_framework/validators.py index ef23b9bd7..84af0b9d5 100644 --- a/rest_framework/validators.py +++ b/rest_framework/validators.py @@ -42,10 +42,11 @@ class UniqueValidator(object): """ message = _('This field must be unique.') - def __init__(self, queryset, message=None): + def __init__(self, queryset, message=None, lookup='exact'): self.queryset = queryset self.serializer_field = None self.message = message or self.message + self.lookup = lookup def set_context(self, serializer_field): """ @@ -62,7 +63,7 @@ class UniqueValidator(object): """ Filter the queryset to all instances matching the given attribute. """ - filter_kwargs = {self.field_name: value} + filter_kwargs = {'%s__%s' % (self.field_name, self.lookup): value} return qs_filter(queryset, **filter_kwargs) def exclude_current_instance(self, queryset): diff --git a/tests/test_validators.py b/tests/test_validators.py index ebc8b899f..ab2c87c11 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -31,7 +31,7 @@ class RelatedModel(models.Model): class RelatedModelSerializer(serializers.ModelSerializer): username = serializers.CharField(source='user.username', - validators=[UniqueValidator(queryset=UniquenessModel.objects.all())]) # NOQA + validators=[UniqueValidator(queryset=UniquenessModel.objects.all(), lookup='iexact')]) # NOQA class Meta: model = RelatedModel @@ -103,7 +103,7 @@ class TestUniquenessValidation(TestCase): AnotherUniquenessModel._meta.get_field('code').validators, []) def test_related_model_is_unique(self): - data = {'username': 'existing', 'email': 'new-email@example.com'} + data = {'username': 'Existing', 'email': 'new-email@example.com'} rs = RelatedModelSerializer(data=data) self.assertFalse(rs.is_valid()) self.assertEqual(rs.errors, From 915ac22aeb5cce447ea20863c594387d71159826 Mon Sep 17 00:00:00 2001 From: Alex Kahan Date: Tue, 4 Oct 2016 16:22:56 -0400 Subject: [PATCH 124/175] Adding tests for rest_framework.py (#4523) --- tests/test_templatetags.py | 145 ++++++++++++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 1 deletion(-) diff --git a/tests/test_templatetags.py b/tests/test_templatetags.py index ac218df21..28390320b 100644 --- a/tests/test_templatetags.py +++ b/tests/test_templatetags.py @@ -3,14 +3,24 @@ from __future__ import unicode_literals from django.test import TestCase +from rest_framework.relations import Hyperlink from rest_framework.templatetags.rest_framework import ( - add_query_param, urlize_quoted_links + add_nested_class, add_query_param, format_value, urlize_quoted_links ) from rest_framework.test import APIRequestFactory factory = APIRequestFactory() +def format_html(html): + """ + Helper function that formats HTML in order for easier comparison + :param html: raw HTML text to be formatted + :return: Cleaned HTML with no newlines or spaces + """ + return html.replace('\n', '').replace(' ', '') + + class TemplateTagTests(TestCase): def test_add_query_param_with_non_latin_character(self): @@ -22,6 +32,139 @@ class TemplateTagTests(TestCase): self.assertIn("q=%E6%9F%A5%E8%AF%A2", json_url) self.assertIn("format=json", json_url) + def test_format_value_boolean_or_none(self): + """ + Tests format_value with booleans and None + """ + self.assertEqual(format_value(True), 'true') + self.assertEqual(format_value(False), 'false') + self.assertEqual(format_value(None), 'null') + + def test_format_value_hyperlink(self): + url = 'http://url.com' + name = 'name_of_url' + hyperlink = Hyperlink(url, name) + self.assertEqual(format_value(hyperlink), '%s' % (url, name)) + + def test_format_value_list(self): + """ + Tests format_value with a list of strings + """ + list_items = ['item1', 'item2', 'item3'] + self.assertEqual(format_value(list_items), '\n item1, item2, item3\n') + self.assertEqual(format_value([]), '\n\n') + + def test_format_value_table(self): + """ + Tests format_value with a list of lists/dicts + """ + list_of_lists = [['list1'], ['list2'], ['list3']] + expected_list_format = """ + + + + 0 + list1 + + + 1 + list2 + + + 2 + list3 + + + """ + self.assertEqual( + format_html(format_value(list_of_lists)), + format_html(expected_list_format) + ) + + expected_dict_format = """ + + + + 0 + + + + 1 + + + + 2 + + + + """ + + list_of_dicts = [{'item1': 'value1'}, {'item2': 'value2'}, {'item3': 'value3'}] + self.assertEqual( + format_html(format_value(list_of_dicts)), + format_html(expected_dict_format) + ) + + def test_format_value_simple_string(self): + """ + Tests format_value with a simple string + """ + simple_string = 'this is an example of a string' + self.assertEqual(format_value(simple_string), simple_string) + + def test_format_value_string_hyperlink(self): + """ + Tests format_value with a url + """ + url = 'http://www.example.com' + self.assertEqual(format_value(url), 'http://www.example.com') + + def test_format_value_string_email(self): + """ + Tests format_value with an email address + """ + email = 'something@somewhere.com' + self.assertEqual(format_value(email), 'something@somewhere.com') + + def test_format_value_string_newlines(self): + """ + Tests format_value with a string with newline characters + :return: + """ + text = 'Dear user, \n this is a message \n from,\nsomeone' + self.assertEqual(format_value(text), '
Dear user, \n this is a message \n from,\nsomeone
') + + def test_format_value_object(self): + """ + Tests that format_value with a object returns the object's __str__ method + """ + obj = object() + self.assertEqual(format_value(obj), obj.__str__()) + + def test_add_nested_class(self): + """ + Tests that add_nested_class returns the proper class + """ + positive_cases = [ + [['item']], + [{'item1': 'value1'}], + {'item1': 'value1'} + ] + + negative_cases = [ + ['list'], + '', + None, + True, + False + ] + + for case in positive_cases: + self.assertEqual(add_nested_class(case), 'class=nested') + + for case in negative_cases: + self.assertEqual(add_nested_class(case), '') + class Issue1386Tests(TestCase): """ From 4ff9e96b4c3e75b9f799c7c401a4560f97e21b3a Mon Sep 17 00:00:00 2001 From: Alex Kahan Date: Wed, 5 Oct 2016 04:54:59 -0400 Subject: [PATCH 125/175] Adding tests to encoder.py (#4536) --- tests/test_encoders.py | 81 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 tests/test_encoders.py diff --git a/tests/test_encoders.py b/tests/test_encoders.py new file mode 100644 index 000000000..d6f681932 --- /dev/null +++ b/tests/test_encoders.py @@ -0,0 +1,81 @@ +from datetime import date, datetime, timedelta, tzinfo +from decimal import Decimal +from uuid import uuid4 + +from django.test import TestCase + +from rest_framework.utils.encoders import JSONEncoder + + +class JSONEncoderTests(TestCase): + """ + Tests the JSONEncoder method + """ + + def setUp(self): + self.encoder = JSONEncoder() + + def test_encode_decimal(self): + """ + Tests encoding a decimal + """ + d = Decimal(3.14) + self.assertEqual(d, float(d)) + + def test_encode_datetime(self): + """ + Tests encoding a datetime object + """ + current_time = datetime.now() + self.assertEqual(self.encoder.default(current_time), current_time.isoformat()) + + def test_encode_time(self): + """ + Tests encoding a timezone + """ + current_time = datetime.now().time() + self.assertEqual(self.encoder.default(current_time), current_time.isoformat()[:12]) + + def test_encode_time_tz(self): + """ + Tests encoding a timezone aware timestamp + """ + + class UTC(tzinfo): + """ + Class extending tzinfo to mimic UTC time + """ + def utcoffset(self, dt): + return timedelta(0) + + def tzname(self, dt): + return "UTC" + + def dst(self, dt): + return timedelta(0) + + current_time = datetime.now().time() + current_time = current_time.replace(tzinfo=UTC()) + with self.assertRaises(ValueError): + self.encoder.default(current_time) + + def test_encode_date(self): + """ + Tests encoding a date object + """ + current_date = date.today() + self.assertEqual(self.encoder.default(current_date), current_date.isoformat()) + + def test_encode_timedelta(self): + """ + Tests encoding a timedelta object + """ + delta = timedelta(hours=1) + self.assertEqual(self.encoder.default(delta), str(delta.total_seconds())) + + def test_encode_uuid(self): + """ + Tests encoding a UUID object + """ + unique_id = uuid4() + self.assertEqual(self.encoder.default(unique_id), str(unique_id)) From e99b30d28b9173c38bb238b6d5af50a1797884eb Mon Sep 17 00:00:00 2001 From: Camille Harang Date: Mon, 10 Oct 2016 12:59:02 +0200 Subject: [PATCH 126/175] =?UTF-8?q?Fix=20rest=5Fframework.filters.Ordering?= =?UTF-8?q?Filter=20doesn't=20pass=20context=20to=20ser=E2=80=A6=20(#4543)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix rest_framework.filters.OrderingFilter doesn't pass context to serializers #4541 * #4541 Additional fix for remove_invalid_fields() --- rest_framework/filters.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index aaf313491..0050ee7cd 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -252,7 +252,7 @@ class OrderingFilter(BaseFilterBackend): params = request.query_params.get(self.ordering_param) if params: fields = [param.strip() for param in params.split(',')] - ordering = self.remove_invalid_fields(queryset, fields, view) + ordering = self.remove_invalid_fields(queryset, fields, view, request) if ordering: return ordering @@ -265,7 +265,7 @@ class OrderingFilter(BaseFilterBackend): return (ordering,) return ordering - def get_default_valid_fields(self, queryset, view): + def get_default_valid_fields(self, queryset, view, context={}): # If `ordering_fields` is not specified, then we determine a default # based on the serializer class, if one exists on the view. if hasattr(view, 'get_serializer_class'): @@ -288,16 +288,16 @@ class OrderingFilter(BaseFilterBackend): return [ (field.source or field_name, field.label) - for field_name, field in serializer_class().fields.items() + for field_name, field in serializer_class(context=context).fields.items() if not getattr(field, 'write_only', False) and not field.source == '*' ] - def get_valid_fields(self, queryset, view): + def get_valid_fields(self, queryset, view, context={}): valid_fields = getattr(view, 'ordering_fields', self.ordering_fields) if valid_fields is None: # Default to allowing filtering on serializer fields - return self.get_default_valid_fields(queryset, view) + return self.get_default_valid_fields(queryset, view, context) elif valid_fields == '__all__': # View explicitly allows filtering on any model field @@ -316,8 +316,8 @@ class OrderingFilter(BaseFilterBackend): return valid_fields - def remove_invalid_fields(self, queryset, fields, view): - valid_fields = [item[0] for item in self.get_valid_fields(queryset, view)] + def remove_invalid_fields(self, queryset, fields, view, request): + valid_fields = [item[0] for item in self.get_valid_fields(queryset, view, {'request': request})] return [term for term in fields if term.lstrip('-') in valid_fields] def filter_queryset(self, request, queryset, view): @@ -332,15 +332,16 @@ class OrderingFilter(BaseFilterBackend): current = self.get_ordering(request, queryset, view) current = None if current is None else current[0] options = [] - for key, label in self.get_valid_fields(queryset, view): - options.append((key, '%s - %s' % (label, _('ascending')))) - options.append(('-' + key, '%s - %s' % (label, _('descending')))) - return { + context = { 'request': request, 'current': current, 'param': self.ordering_param, - 'options': options, } + for key, label in self.get_valid_fields(queryset, view, context): + options.append((key, '%s - %s' % (label, _('ascending')))) + options.append(('-' + key, '%s - %s' % (label, _('descending')))) + context['options'] = options + return context def to_html(self, request, queryset, view): template = loader.get_template(self.template) From d49e26f1279adb8f36818358281279dc124522c6 Mon Sep 17 00:00:00 2001 From: Manjit Kumar Date: Mon, 10 Oct 2016 17:05:38 +0530 Subject: [PATCH 127/175] add drf-url-filters in third party filtering resources and fixed typo (#4548) --- docs/api-guide/filtering.md | 2 +- docs/topics/third-party-resources.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index bc502e970..ab7e5fa77 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -434,7 +434,7 @@ The [djangorestframework-word-filter][django-rest-framework-word-search-filter] ## drf-url-filters -[drf-url-filter][drf-url-filter] is a simple Django app to apply filters on drf `ModelViewSet`'s `Queryset` in a clean, simple and configurable way. It also supports validations on incoming query params and their values. A beautiful python package `Voluptouos` is being used for validations on the incoming query parameters. The best part about voluptouos is you can define your own validations as per your query params requirements. +[drf-url-filter][drf-url-filter] is a simple Django app to apply filters on drf `ModelViewSet`'s `Queryset` in a clean, simple and configurable way. It also supports validations on incoming query params and their values. A beautiful python package `Voluptuous` is being used for validations on the incoming query parameters. The best part about voluptuous is you can define your own validations as per your query params requirements. [cite]: https://docs.djangoproject.com/en/dev/topics/db/queries/#retrieving-specific-objects-with-filters [django-filter]: https://github.com/alex/django-filter diff --git a/docs/topics/third-party-resources.md b/docs/topics/third-party-resources.md index 4366269bb..3fba9b5da 100644 --- a/docs/topics/third-party-resources.md +++ b/docs/topics/third-party-resources.md @@ -238,6 +238,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque * [djangorestframework-chain][djangorestframework-chain] - Allows arbitrary chaining of both relations and lookup filters. * [django-url-filter][django-url-filter] - Allows a safe way to filter data via human-friendly URLs. It is a generic library which is not tied to DRF but it provides easy integration with DRF. +* [drf-url-filter][drf-url-filter] is a simple Django app to apply filters on drf `ModelViewSet`'s `Queryset` in a clean, simple and configurable way. It also supports validations on incoming query params and their values. ### Misc @@ -351,6 +352,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque [django-rest-framework-braces]: https://github.com/dealertrack/django-rest-framework-braces [dry-rest-permissions]: https://github.com/Helioscene/dry-rest-permissions [django-url-filter]: https://github.com/miki725/django-url-filter +[drf-url-filter]: https://github.com/manjitkumar/drf-url-filters [cookiecutter-django-rest]: https://github.com/agconti/cookiecutter-django-rest [drf-haystack]: https://drf-haystack.readthedocs.io/en/latest/ [django-rest-framework-version-transforms]: https://github.com/mrhwick/django-rest-framework-version-transforms From 0dec36eb413c98902d997ebd2f9cb2b7111e6e56 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 10 Oct 2016 13:03:46 +0100 Subject: [PATCH 128/175] Version 3.5 (#4525) * Start test case * Added 'requests' test client * Address typos * Graceful fallback if requests is not installed. * Add cookie support * Tests for auth and CSRF * Py3 compat * py3 compat * py3 compat * Add get_requests_client * Added SchemaGenerator.should_include_link * add settings for html cutoff on related fields * Router doesn't work if prefix is blank, though project urls.py handles prefix * Fix Django 1.10 to-many deprecation * Add django.core.urlresolvers compatibility * Update django-filter & django-guardian * Check for empty router prefix; adjust URL accordingly It's easiest to fix this issue after we have made the regex. To try to fix it before would require doing something different for List vs Detail, which means we'd have to know which type of url we're constructing before acting accordingly. * Fix misc django deprecations * Use TOC extension instead of header * Fix deprecations for py3k * Add py3k compatibility to is_simple_callable * Add is_simple_callable tests * Drop python 3.2 support (EOL, Dropped by Django) * schema_renderers= should *set* the renderers, not append to them. * API client (#4424) * Fix release notes * Add note about 'User account is disabled.' vs 'Unable to log in' * Clean up schema generation (#4527) * Handle multiple methods on custom action (#4529) * RequestsClient, CoreAPIClient * exclude_from_schema * Added 'get_schema_view()' shortcut * Added schema descriptions * Better descriptions for schemas * Add type annotation to schema generation * Coerce schema 'pk' in path to actual field name * Deprecations move into assertion errors * Use get_schema_view in tests * Updte CoreJSON media type * Handle schema structure correctly when path prefixs exist. Closes #4401 * Add PendingDeprecation to Router schema generation. * Added SCHEMA_COERCE_PATH_PK and SCHEMA_COERCE_METHOD_NAMES * Renamed and documented 'get_schema_fields' interface. --- .travis.yml | 1 - docs/api-guide/filtering.md | 6 + docs/api-guide/pagination.md | 6 + docs/api-guide/relations.md | 2 + docs/api-guide/reverse.md | 4 +- docs/api-guide/schemas.md | 140 +++-- docs/api-guide/settings.md | 38 ++ docs/api-guide/testing.md | 95 ++- docs/api-guide/views.md | 12 +- docs/tutorial/1-serialization.md | 14 +- docs/tutorial/2-requests-and-responses.md | 8 +- docs/tutorial/3-class-based-views.md | 18 +- .../4-authentication-and-permissions.md | 4 +- .../5-relationships-and-hyperlinked-apis.md | 14 +- docs/tutorial/6-viewsets-and-routers.md | 6 +- .../7-schemas-and-client-libraries.md | 47 +- requirements/requirements-optionals.txt | 6 +- rest_framework/__init__.py | 2 +- rest_framework/authtoken/serializers.py | 3 + rest_framework/compat.py | 40 +- rest_framework/decorators.py | 3 +- rest_framework/fields.py | 38 +- rest_framework/filters.py | 28 +- rest_framework/pagination.py | 34 +- rest_framework/relations.py | 37 +- rest_framework/renderers.py | 6 +- rest_framework/reverse.py | 6 +- rest_framework/routers.py | 79 ++- rest_framework/schemas.py | 551 +++++++++++++----- rest_framework/serializers.py | 29 +- rest_framework/settings.py | 11 + rest_framework/templatetags/rest_framework.py | 3 +- rest_framework/test.py | 127 ++++ rest_framework/urlpatterns.py | 2 +- rest_framework/utils/breadcrumbs.py | 2 +- rest_framework/views.py | 4 + tests/browsable_api/test_form_rendering.py | 1 + tests/conftest.py | 15 +- tests/test_api_client.py | 474 +++++++++++++++ tests/test_atomic_requests.py | 32 +- tests/test_authentication.py | 3 +- tests/test_fields.py | 62 ++ tests/test_filters.py | 6 +- tests/test_model_serializer.py | 7 +- tests/test_permissions.py | 11 +- tests/test_request.py | 5 +- tests/test_requests_client.py | 256 ++++++++ tests/test_reverse.py | 2 +- tests/test_routers.py | 49 ++ tests/test_schemas.py | 217 +++++-- tests/test_urlpatterns.py | 8 +- tests/utils.py | 2 +- tox.ini | 3 +- 53 files changed, 2132 insertions(+), 447 deletions(-) create mode 100644 tests/test_api_client.py create mode 100644 tests/test_requests_client.py diff --git a/.travis.yml b/.travis.yml index c9d9a1648..100a7cd8b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,6 @@ env: - TOX_ENV=py35-django18 - TOX_ENV=py34-django18 - TOX_ENV=py33-django18 - - TOX_ENV=py32-django18 - TOX_ENV=py27-django18 - TOX_ENV=py27-django110 - TOX_ENV=py35-django110 diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index ab7e5fa77..40a097174 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -416,6 +416,12 @@ Generic filters may also present an interface in the browsable API. To do so you The method should return a rendered HTML string. +## Pagination & schemas + +You can also make the filter controls available to the schema autogeneration +that REST framework provides, by implementing a `get_schema_fields()` method, +which should return a list of `coreapi.Field` instances. + # Third party packages The following third party packages provide additional filter implementations. diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index 088e07184..f990128c5 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -276,6 +276,12 @@ To have your custom pagination class be used by default, use the `DEFAULT_PAGINA 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: +## Pagination & schemas + +You can also make the pagination controls available to the schema autogeneration +that REST framework provides, by implementing a `get_schema_fields()` method, +which should return a list of `coreapi.Field` instances. + --- ![Link Header][link-header] diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md index 42533823c..8fde53d3a 100644 --- a/docs/api-guide/relations.md +++ b/docs/api-guide/relations.md @@ -463,6 +463,8 @@ There are two keyword arguments you can use to control this behavior: - `html_cutoff` - If set this will be the maximum number of choices that will be displayed by a HTML select drop down. Set to `None` to disable any limiting. Defaults to `1000`. - `html_cutoff_text` - If set this will display a textual indicator if the maximum number of items have been cutoff in an HTML select drop down. Defaults to `"More than {count} items…"` +You can also control these globally using the settings `HTML_SELECT_CUTOFF` and `HTML_SELECT_CUTOFF_TEXT`. + In cases where the cutoff is being enforced you may want to instead use a plain input field in the HTML form. You can do so using the `style` keyword argument. For example: assigned_to = serializers.SlugRelatedField( diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md index 71fb83f9e..35d88e2db 100644 --- a/docs/api-guide/reverse.md +++ b/docs/api-guide/reverse.md @@ -23,7 +23,7 @@ There's no requirement for you to use them, but if you do then the self-describi **Signature:** `reverse(viewname, *args, **kwargs)` -Has the same behavior as [`django.core.urlresolvers.reverse`][reverse], except that it returns a fully qualified URL, using the request to determine the host and port. +Has the same behavior as [`django.urls.reverse`][reverse], except that it returns a fully qualified URL, using the request to determine the host and port. You should **include the request as a keyword argument** to the function, for example: @@ -44,7 +44,7 @@ You should **include the request as a keyword argument** to the function, for ex **Signature:** `reverse_lazy(viewname, *args, **kwargs)` -Has the same behavior as [`django.core.urlresolvers.reverse_lazy`][reverse-lazy], except that it returns a fully qualified URL, using the request to determine the host and port. +Has the same behavior as [`django.urls.reverse_lazy`][reverse-lazy], except that it returns a fully qualified URL, using the request to determine the host and port. As with the `reverse` function, you should **include the request as a keyword argument** to the function, for example: diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 7f8af723e..16d2bbb01 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -102,15 +102,20 @@ REST framework includes functionality for auto-generating a schema, or allows you to specify one explicitly. There are a few different ways to add a schema to your API, depending on exactly what you need. -## Using DefaultRouter +## The get_schema_view shortcut -If you're using `DefaultRouter` then you can include an auto-generated schema, -simply by adding a `schema_title` argument to the router. +The simplest way to include a schema in your project is to use the +`get_schema_view()` function. - router = DefaultRouter(schema_title='Server Monitoring API') + schema_view = get_schema_view(title="Server Monitoring API") -The schema will be included at the root URL, `/`, and presented to clients -that include the Core JSON media type in their `Accept` header. + urlpatterns = [ + url('^$', schema_view), + ... + ] + +Once the view has been added, you'll be able to make API requests to retrieve +the auto-generated schema definition. $ http http://127.0.0.1:8000/ Accept:application/vnd.coreapi+json HTTP/1.0 200 OK @@ -125,48 +130,43 @@ that include the Core JSON media type in their `Accept` header. ... } -This is a great zero-configuration option for when you want to get up and -running really quickly. +The arguments to `get_schema_view()` are: -The other available options to `DefaultRouter` are: +#### `title` -#### schema_renderers +May be used to provide a descriptive title for the schema definition. -May be used to pass the set of renderer classes that can be used to render schema output. +#### `url` + +May be used to pass a canonical URL for the schema. + + schema_view = get_schema_view( + title='Server Monitoring API', + url='https://www.example.org/api/' + ) + +#### `renderer_classes` + +May be used to pass the set of renderer classes that can be used to render the API root endpoint. from rest_framework.renderers import CoreJSONRenderer from my_custom_package import APIBlueprintRenderer - router = DefaultRouter(schema_title='Server Monitoring API', schema_renderers=[ - CoreJSONRenderer, APIBlueprintRenderer - ]) - -#### schema_url - -May be used to pass the root URL for the schema. This can either be used to ensure that -the schema URLs include a canonical hostname and schema, or to ensure that all the -schema URLs include a path prefix. - - router = DefaultRouter( - schema_title='Server Monitoring API', - schema_url='https://www.example.org/api/' + schema_view = get_schema_view( + title='Server Monitoring API', + url='https://www.example.org/api/', + renderer_classes=[CoreJSONRenderer, APIBlueprintRenderer] ) -If you want more flexibility over the schema output then you'll need to consider -using `SchemaGenerator` instead. +## Using an explicit schema view -#### root_renderers - -May be used to pass the set of renderer classes that can be used to render the API root endpoint. - -## Using SchemaGenerator - -The most common way to add a schema to your API is to use the `SchemaGenerator` -class to auto-generate the `Document` instance, and to return that from a view. +If you need a little more control than the `get_schema_view()` shortcut gives you, +then you can use the `SchemaGenerator` class directly to auto-generate the +`Document` instance, and to return that from a view. This option gives you the flexibility of setting up the schema endpoint -with whatever behavior you want. For example, you can apply different -permission, throttling or authentication policies to the schema endpoint. +with whatever behaviour you want. For example, you can apply different +permission, throttling, or authentication policies to the schema endpoint. Here's an example of using `SchemaGenerator` together with a view to return the schema. @@ -176,12 +176,13 @@ return the schema. from rest_framework.decorators import api_view, renderer_classes from rest_framework import renderers, response, schemas + generator = schemas.SchemaGenerator(title='Bookings API') @api_view() @renderer_classes([renderers.CoreJSONRenderer]) def schema_view(request): - generator = schemas.SchemaGenerator(title='Bookings API') - return response.Response(generator.get_schema()) + schema = generator.get_schema(request) + return response.Response(schema) **urls.py:** @@ -241,6 +242,69 @@ You could then either: --- +# Schemas as documentation + +One common usage of API schemas is to use them to build documentation pages. + +The schema generation in REST framework uses docstrings to automatically +populate descriptions in the schema document. + +These descriptions will be based on: + +* The corresponding method docstring if one exists. +* A named section within the class docstring, which can be either single line or multi-line. +* The class docstring. + +## Examples + +An `APIView`, with an explicit method docstring. + + class ListUsernames(APIView): + def get(self, request): + """ + Return a list of all user names in the system. + """ + usernames = [user.username for user in User.objects.all()] + return Response(usernames) + +A `ViewSet`, with an explict action docstring. + + class ListUsernames(ViewSet): + def list(self, request): + """ + Return a list of all user names in the system. + """ + usernames = [user.username for user in User.objects.all()] + return Response(usernames) + +A generic view with sections in the class docstring, using single-line style. + + class UserList(generics.ListCreateAPIView): + """ + get: Create a new user. + post: List all the users. + """ + queryset = User.objects.all() + serializer_class = UserSerializer + permission_classes = (IsAdminUser,) + +A generic viewset with sections in the class docstring, using multi-line style. + + class UserViewSet(viewsets.ModelViewSet): + """ + API endpoint that allows users to be viewed or edited. + + retrieve: + Return a user instance. + + list: + Return all users, ordered by most recently joined. + """ + queryset = User.objects.all().order_by('-date_joined') + serializer_class = UserSerializer + +--- + # Alternate schema formats In order to support an alternate schema format, you need to implement a custom renderer diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index ea018053f..58ceeeeb4 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -234,6 +234,28 @@ Default: --- +## Schema generation controls + +#### SCHEMA_COERCE_PATH_PK + +If set, this maps the `'pk'` identifier in the URL conf onto the actual field +name when generating a schema path parameter. Typically this will be `'id'`. +This gives a more suitable representation as "primary key" is an implementation +detail, wheras "identifier" is a more general concept. + +Default: `True` + +#### SCHEMA_COERCE_METHOD_NAMES + +If set, this is used to map internal viewset method names onto external action +names used in the schema generation. This allows us to generate names that +are more suitable for an external representation than those that are used +internally in the codebase. + +Default: `{'retrieve': 'read', 'destroy': 'delete'}` + +--- + ## Content type controls #### URL_FORMAT_OVERRIDE @@ -382,6 +404,22 @@ This should be a function with the following signature: Default: `'rest_framework.views.get_view_description'` +## HTML Select Field cutoffs + +Global settings for [select field cutoffs for rendering relational fields](relations.md#select-field-cutoffs) in the browsable API. + +#### HTML_SELECT_CUTOFF + +Global setting for the `html_cutoff` value. Must be an integer. + +Default: 1000 + +#### HTML_SELECT_CUTOFF_TEXT + +A string representing a global setting for `html_cutoff_text`. + +Default: `"More than {count} items..."` + --- ## Miscellaneous settings diff --git a/docs/api-guide/testing.md b/docs/api-guide/testing.md index 69da7d105..fdd60b7f4 100644 --- a/docs/api-guide/testing.md +++ b/docs/api-guide/testing.md @@ -184,6 +184,99 @@ As usual CSRF validation will only apply to any session authenticated views. Th --- +# RequestsClient + +REST framework also includes a client for interacting with your application +using the popular Python library, `requests`. + +This exposes exactly the same interface as if you were using a requests session +directly. + + client = RequestsClient() + response = client.get('http://testserver/users/') + +Note that the requests client requires you to pass fully qualified URLs. + +## Headers & Authentication + +Custom headers and authentication credentials can be provided in the same way +as [when using a standard `requests.Session` instance](http://docs.python-requests.org/en/master/user/advanced/#session-objects). + + from requests.auth import HTTPBasicAuth + + client.auth = HTTPBasicAuth('user', 'pass') + client.headers.update({'x-test': 'true'}) + +## CSRF + +If you're using `SessionAuthentication` then you'll need to include a CSRF token +for any `POST`, `PUT`, `PATCH` or `DELETE` requests. + +You can do so by following the same flow that a JavaScript based client would use. +First make a `GET` request in order to obtain a CRSF token, then present that +token in the following request. + +For example... + + client = RequestsClient() + + # Obtain a CSRF token. + response = client.get('/homepage/') + assert response.status_code == 200 + csrftoken = response.cookies['csrftoken'] + + # Interact with the API. + response = client.post('/organisations/', json={ + 'name': 'MegaCorp', + 'status': 'active' + }, headers={'X-CSRFToken': csrftoken}) + assert response.status_code == 200 + +## Live tests + +With careful usage both the `RequestsClient` and the `CoreAPIClient` provide +the ability to write test cases that can run either in development, or be run +directly against your staging server or production environment. + +Using this style to create basic tests of a few core piece of functionality is +a powerful way to validate your live service. Doing so may require some careful +attention to setup and teardown to ensure that the tests run in a way that they +do not directly affect customer data. + +--- + +# CoreAPIClient + +The CoreAPIClient allows you to interact with your API using the Python +`coreapi` client library. + + # Fetch the API schema + url = reverse('schema') + client = CoreAPIClient() + schema = client.get(url) + + # Create a new organisation + params = {'name': 'MegaCorp', 'status': 'active'} + client.action(schema, ['organisations', 'create'], params) + + # Ensure that the organisation exists in the listing + data = client.action(schema, ['organisations', 'list']) + assert(len(data) == 1) + assert(data == [{'name': 'MegaCorp', 'status': 'active'}]) + +## Headers & Authentication + +Custom headers and authentication may be used with `CoreAPIClient` in a +similar way as with `RequestsClient`. + + from requests.auth import HTTPBasicAuth + + client = CoreAPIClient() + client.session.auth = HTTPBasicAuth('user', 'pass') + client.session.headers.update({'x-test': 'true'}) + +--- + # Test cases REST framework includes the following test case classes, that mirror the existing Django test case classes, but use `APIClient` instead of Django's default `Client`. @@ -197,7 +290,7 @@ REST framework includes the following test case classes, that mirror the existin You can use any of REST framework's test case classes as you would for the regular Django test case classes. The `self.client` attribute will be an `APIClient` instance. - from django.core.urlresolvers import reverse + from django.urls import reverse from rest_framework import status from rest_framework.test import APITestCase from myproject.apps.core.models import Account diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md index 62f14087f..55f6664e0 100644 --- a/docs/api-guide/views.md +++ b/docs/api-guide/views.md @@ -127,7 +127,7 @@ REST framework also allows you to work with regular function based views. It pr ## @api_view() -**Signature:** `@api_view(http_method_names=['GET'])` +**Signature:** `@api_view(http_method_names=['GET'], exclude_from_schema=False)` The core of this functionality is the `api_view` decorator, which takes a list of HTTP methods that your view should respond to. For example, this is how you would write a very simple view that just manually returns some data: @@ -139,7 +139,7 @@ The core of this functionality is the `api_view` decorator, which takes a list o This view will use the default renderers, parsers, authentication classes etc specified in the [settings]. -By default only `GET` methods will be accepted. Other methods will respond with "405 Method Not Allowed". To alter this behavior, specify which methods the view allows, like so: +By default only `GET` methods will be accepted. Other methods will respond with "405 Method Not Allowed". To alter this behaviour, specify which methods the view allows, like so: @api_view(['GET', 'POST']) def hello_world(request): @@ -147,6 +147,13 @@ By default only `GET` methods will be accepted. Other methods will respond with return Response({"message": "Got some data!", "data": request.data}) return Response({"message": "Hello, world!"}) +You can also mark an API view as being omitted from any [auto-generated schema][schemas], +using the `exclude_from_schema` argument.: + + @api_view(['GET'], exclude_from_schema=True) + def api_docs(request): + ... + ## API policy decorators To override the default settings, REST framework provides a set of additional decorators which can be added to your views. These must come *after* (below) the `@api_view` decorator. For example, to create a view that uses a [throttle][throttling] to ensure it can only be called once per day by a particular user, use the `@throttle_classes` decorator, passing a list of throttle classes: @@ -178,3 +185,4 @@ Each of these decorators takes a single argument which must be a list or tuple o [cite2]: http://www.boredomandlaziness.org/2012/05/djangos-cbvs-are-not-mistake-but.html [settings]: settings.md [throttling]: throttling.md +[schemas]: schemas.md diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index 87856e037..434072e11 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -88,7 +88,7 @@ The first thing we need to get started on our Web API is to provide a way of ser class SnippetSerializer(serializers.Serializer): - pk = serializers.IntegerField(read_only=True) + id = serializers.IntegerField(read_only=True) title = serializers.CharField(required=False, allow_blank=True, max_length=100) code = serializers.CharField(style={'base_template': 'textarea.html'}) linenos = serializers.BooleanField(required=False) @@ -144,13 +144,13 @@ We've now got a few snippet instances to play with. Let's take a look at serial serializer = SnippetSerializer(snippet) serializer.data - # {'pk': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'} + # {'id': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'} At this point we've translated the model instance into Python native datatypes. To finalize the serialization process we render the data into `json`. content = JSONRenderer().render(serializer.data) content - # '{"pk": 2, "title": "", "code": "print \\"hello, world\\"\\n", "linenos": false, "language": "python", "style": "friendly"}' + # '{"id": 2, "title": "", "code": "print \\"hello, world\\"\\n", "linenos": false, "language": "python", "style": "friendly"}' Deserialization is similar. First we parse a stream into Python native datatypes... @@ -175,7 +175,7 @@ We can also serialize querysets instead of model instances. To do so we simply serializer = SnippetSerializer(Snippet.objects.all(), many=True) serializer.data - # [OrderedDict([('pk', 1), ('title', u''), ('code', u'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('pk', 2), ('title', u''), ('code', u'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('pk', 3), ('title', u''), ('code', u'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])] + # [OrderedDict([('id', 1), ('title', u''), ('code', u'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', u''), ('code', u'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', u''), ('code', u'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])] ## Using ModelSerializers @@ -259,12 +259,12 @@ Note that because we want to be able to POST to this view from clients that won' We'll also need a view which corresponds to an individual snippet, and can be used to retrieve, update or delete the snippet. @csrf_exempt - def snippet_detail(request, pk): + def snippet_detail(request, id): """ Retrieve, update or delete a code snippet. """ try: - snippet = Snippet.objects.get(pk=pk) + snippet = Snippet.objects.get(id=id) except Snippet.DoesNotExist: return HttpResponse(status=404) @@ -291,7 +291,7 @@ Finally we need to wire these views up. Create the `snippets/urls.py` file: urlpatterns = [ url(r'^snippets/$', views.snippet_list), - url(r'^snippets/(?P[0-9]+)/$', views.snippet_detail), + url(r'^snippets/(?P[0-9]+)/$', views.snippet_detail), ] We also need to wire up the root urlconf, in the `tutorial/urls.py` file, to include our snippet app's URLs. diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md index 5c020a1f7..4aa0062a3 100644 --- a/docs/tutorial/2-requests-and-responses.md +++ b/docs/tutorial/2-requests-and-responses.md @@ -66,12 +66,12 @@ Our instance view is an improvement over the previous example. It's a little mo Here is the view for an individual snippet, in the `views.py` module. @api_view(['GET', 'PUT', 'DELETE']) - def snippet_detail(request, pk): + def snippet_detail(request, id): """ Retrieve, update or delete a snippet instance. """ try: - snippet = Snippet.objects.get(pk=pk) + snippet = Snippet.objects.get(id=id) except Snippet.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) @@ -104,7 +104,7 @@ Start by adding a `format` keyword argument to both of the views, like so. and - def snippet_detail(request, pk, format=None): + def snippet_detail(request, id, format=None): Now update the `urls.py` file slightly, to append a set of `format_suffix_patterns` in addition to the existing URLs. @@ -114,7 +114,7 @@ Now update the `urls.py` file slightly, to append a set of `format_suffix_patter urlpatterns = [ url(r'^snippets/$', views.snippet_list), - url(r'^snippets/(?P[0-9]+)$', views.snippet_detail), + url(r'^snippets/(?P[0-9]+)$', views.snippet_detail), ] urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/docs/tutorial/3-class-based-views.md b/docs/tutorial/3-class-based-views.md index f018666f5..6303994cd 100644 --- a/docs/tutorial/3-class-based-views.md +++ b/docs/tutorial/3-class-based-views.md @@ -36,27 +36,27 @@ So far, so good. It looks pretty similar to the previous case, but we've got be """ Retrieve, update or delete a snippet instance. """ - def get_object(self, pk): + def get_object(self, id): try: - return Snippet.objects.get(pk=pk) + return Snippet.objects.get(id=id) except Snippet.DoesNotExist: raise Http404 - def get(self, request, pk, format=None): - snippet = self.get_object(pk) + def get(self, request, id, format=None): + snippet = self.get_object(id) serializer = SnippetSerializer(snippet) return Response(serializer.data) - def put(self, request, pk, format=None): - snippet = self.get_object(pk) + def put(self, request, id, format=None): + snippet = self.get_object(id) serializer = SnippetSerializer(snippet, data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - def delete(self, request, pk, format=None): - snippet = self.get_object(pk) + def delete(self, request, id, format=None): + snippet = self.get_object(id) snippet.delete() return Response(status=status.HTTP_204_NO_CONTENT) @@ -70,7 +70,7 @@ We'll also need to refactor our `urls.py` slightly now we're using class-based v urlpatterns = [ url(r'^snippets/$', views.SnippetList.as_view()), - url(r'^snippets/(?P[0-9]+)/$', views.SnippetDetail.as_view()), + url(r'^snippets/(?P[0-9]+)/$', views.SnippetDetail.as_view()), ] urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/docs/tutorial/4-authentication-and-permissions.md b/docs/tutorial/4-authentication-and-permissions.md index d69c38552..958f9d3f0 100644 --- a/docs/tutorial/4-authentication-and-permissions.md +++ b/docs/tutorial/4-authentication-and-permissions.md @@ -88,7 +88,7 @@ Make sure to also import the `UserSerializer` class Finally we need to add those views into the API, by referencing them from the URL conf. Add the following to the patterns in `urls.py`. url(r'^users/$', views.UserList.as_view()), - url(r'^users/(?P[0-9]+)/$', views.UserDetail.as_view()), + url(r'^users/(?P[0-9]+)/$', views.UserDetail.as_view()), ## Associating Snippets with Users @@ -150,7 +150,7 @@ The `r'^api-auth/'` part of pattern can actually be whatever URL you want to use Now if you open up the browser again and refresh the page you'll see a 'Login' link in the top right of the page. If you log in as one of the users you created earlier, you'll be able to create code snippets again. -Once you've created a few code snippets, navigate to the '/users/' endpoint, and notice that the representation includes a list of the snippet pks that are associated with each user, in each user's 'snippets' field. +Once you've created a few code snippets, navigate to the '/users/' endpoint, and notice that the representation includes a list of the snippet ids that are associated with each user, in each user's 'snippets' field. ## Object level permissions diff --git a/docs/tutorial/5-relationships-and-hyperlinked-apis.md b/docs/tutorial/5-relationships-and-hyperlinked-apis.md index 8cda09c62..9fb6c53e0 100644 --- a/docs/tutorial/5-relationships-and-hyperlinked-apis.md +++ b/docs/tutorial/5-relationships-and-hyperlinked-apis.md @@ -48,7 +48,7 @@ We'll add a url pattern for our new API root in `snippets/urls.py`: And then add a url pattern for the snippet highlights: - url(r'^snippets/(?P[0-9]+)/highlight/$', views.SnippetHighlight.as_view()), + url(r'^snippets/(?P[0-9]+)/highlight/$', views.SnippetHighlight.as_view()), ## Hyperlinking our API @@ -67,7 +67,7 @@ In this case we'd like to use a hyperlinked style between entities. In order to The `HyperlinkedModelSerializer` has the following differences from `ModelSerializer`: -* It does not include the `pk` field by default. +* It does not include the `id` field by default. * It includes a `url` field, using `HyperlinkedIdentityField`. * Relationships use `HyperlinkedRelatedField`, instead of `PrimaryKeyRelatedField`. @@ -80,7 +80,7 @@ We can easily re-write our existing serializers to use hyperlinking. In your `sn class Meta: model = Snippet - fields = ('url', 'pk', 'highlight', 'owner', + fields = ('url', 'id', 'highlight', 'owner', 'title', 'code', 'linenos', 'language', 'style') @@ -89,7 +89,7 @@ We can easily re-write our existing serializers to use hyperlinking. In your `sn class Meta: model = User - fields = ('url', 'pk', 'username', 'snippets') + fields = ('url', 'id', 'username', 'snippets') Notice that we've also added a new `'highlight'` field. This field is of the same type as the `url` field, except that it points to the `'snippet-highlight'` url pattern, instead of the `'snippet-detail'` url pattern. @@ -116,16 +116,16 @@ After adding all those names into our URLconf, our final `snippets/urls.py` file url(r'^snippets/$', views.SnippetList.as_view(), name='snippet-list'), - url(r'^snippets/(?P[0-9]+)/$', + url(r'^snippets/(?P[0-9]+)/$', views.SnippetDetail.as_view(), name='snippet-detail'), - url(r'^snippets/(?P[0-9]+)/highlight/$', + url(r'^snippets/(?P[0-9]+)/highlight/$', views.SnippetHighlight.as_view(), name='snippet-highlight'), url(r'^users/$', views.UserList.as_view(), name='user-list'), - url(r'^users/(?P[0-9]+)/$', + url(r'^users/(?P[0-9]+)/$', views.UserDetail.as_view(), name='user-detail') ]) diff --git a/docs/tutorial/6-viewsets-and-routers.md b/docs/tutorial/6-viewsets-and-routers.md index 00152cc17..6e1321093 100644 --- a/docs/tutorial/6-viewsets-and-routers.md +++ b/docs/tutorial/6-viewsets-and-routers.md @@ -92,10 +92,10 @@ Now that we've bound our resources into concrete views, we can register the view urlpatterns = format_suffix_patterns([ url(r'^$', api_root), url(r'^snippets/$', snippet_list, name='snippet-list'), - url(r'^snippets/(?P[0-9]+)/$', snippet_detail, name='snippet-detail'), - url(r'^snippets/(?P[0-9]+)/highlight/$', snippet_highlight, name='snippet-highlight'), + url(r'^snippets/(?P[0-9]+)/$', snippet_detail, name='snippet-detail'), + url(r'^snippets/(?P[0-9]+)/highlight/$', snippet_highlight, name='snippet-highlight'), url(r'^users/$', user_list, name='user-list'), - url(r'^users/(?P[0-9]+)/$', user_detail, name='user-detail') + url(r'^users/(?P[0-9]+)/$', user_detail, name='user-detail') ]) ## Using Routers diff --git a/docs/tutorial/7-schemas-and-client-libraries.md b/docs/tutorial/7-schemas-and-client-libraries.md index 77cfdd3b3..705b79da6 100644 --- a/docs/tutorial/7-schemas-and-client-libraries.md +++ b/docs/tutorial/7-schemas-and-client-libraries.md @@ -33,10 +33,17 @@ API schema. $ pip install coreapi -We can now include a schema for our API, by adding a `schema_title` argument to -the router instantiation. +We can now include a schema for our API, by including an autogenerated schema +view in our URL configuration. - router = DefaultRouter(schema_title='Pastebin API') + from rest_framework.schemas import get_schema_view + + schema_view = get_schema_view(title='Pastebin API') + + urlpatterns = [ + url('^schema/$', schema_view), + ... + ] If you visit the API root endpoint in a browser you should now see `corejson` representation become available as an option. @@ -46,7 +53,7 @@ representation become available as an option. We can also request the schema from the command line, by specifying the desired content type in the `Accept` header. - $ http http://127.0.0.1:8000/ Accept:application/vnd.coreapi+json + $ http http://127.0.0.1:8000/schema/ Accept:application/vnd.coreapi+json HTTP/1.0 200 OK Allow: GET, HEAD, OPTIONS Content-Type: application/vnd.coreapi+json @@ -91,16 +98,16 @@ Now check that it is available on the command line... First we'll load the API schema using the command line client. - $ coreapi get http://127.0.0.1:8000/ - + $ coreapi get http://127.0.0.1:8000/schema/ + snippets: { - highlight(pk) + highlight(id) list() - retrieve(pk) + read(id) } users: { list() - retrieve(pk) + read(id) } We haven't authenticated yet, so right now we're only able to see the read only @@ -112,7 +119,7 @@ Let's try listing the existing snippets, using the command line client: [ { "url": "http://127.0.0.1:8000/snippets/1/", - "pk": 1, + "id": 1, "highlight": "http://127.0.0.1:8000/snippets/1/highlight/", "owner": "lucy", "title": "Example", @@ -126,7 +133,7 @@ Let's try listing the existing snippets, using the command line client: Some of the API endpoints require named parameters. For example, to get back the highlight HTML for a particular snippet we need to provide an id. - $ coreapi action snippets highlight --param pk=1 + $ coreapi action snippets highlight --param id=1 @@ -150,19 +157,19 @@ Now if we fetch the schema again, we should be able to see the full set of available interactions. $ coreapi reload - Pastebin API "http://127.0.0.1:8000/"> + Pastebin API "http://127.0.0.1:8000/schema/"> snippets: { create(code, [title], [linenos], [language], [style]) - destroy(pk) - highlight(pk) + delete(id) + highlight(id) list() - partial_update(pk, [title], [code], [linenos], [language], [style]) - retrieve(pk) - update(pk, code, [title], [linenos], [language], [style]) + partial_update(id, [title], [code], [linenos], [language], [style]) + read(id) + update(id, code, [title], [linenos], [language], [style]) } users: { list() - retrieve(pk) + read(id) } We're now able to interact with these endpoints. For example, to create a new @@ -171,7 +178,7 @@ snippet: $ coreapi action snippets create --param title="Example" --param code="print('hello, world')" { "url": "http://127.0.0.1:8000/snippets/7/", - "pk": 7, + "id": 7, "highlight": "http://127.0.0.1:8000/snippets/7/highlight/", "owner": "lucy", "title": "Example", @@ -183,7 +190,7 @@ snippet: And to delete a snippet: - $ coreapi action snippets destroy --param pk=7 + $ coreapi action snippets delete --param id=7 As well as the command line client, developers can also interact with your API using client libraries. The Python client library is the first of these diff --git a/requirements/requirements-optionals.txt b/requirements/requirements-optionals.txt index 20436e6b4..31f24f4b7 100644 --- a/requirements/requirements-optionals.txt +++ b/requirements/requirements-optionals.txt @@ -1,5 +1,5 @@ # Optional packages which may be used with REST framework. markdown==2.6.4 -django-guardian==1.4.3 -django-filter==0.13.0 -coreapi==1.32.0 +django-guardian==1.4.6 +django-filter==0.14.0 +coreapi==2.0.8 diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 457af6c88..68e96703f 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ ______ _____ _____ _____ __ """ __title__ = 'Django REST framework' -__version__ = '3.4.7' +__version__ = '3.5.0' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2016 Tom Christie' diff --git a/rest_framework/authtoken/serializers.py b/rest_framework/authtoken/serializers.py index df0c48b86..90d3bd96e 100644 --- a/rest_framework/authtoken/serializers.py +++ b/rest_framework/authtoken/serializers.py @@ -16,6 +16,9 @@ class AuthTokenSerializer(serializers.Serializer): user = authenticate(username=username, password=password) if user: + # From Django 1.10 onwards the `authenticate` call simply + # returns `None` for is_active=False users. + # (Assuming the default `ModelBackend` authentication backend.) if not user.is_active: msg = _('User account is disabled.') raise serializers.ValidationError(msg) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index cee430a84..7ec39ba63 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -23,6 +23,16 @@ except ImportError: from django.utils import importlib # Will be removed in Django 1.9 +try: + from django.urls import ( + NoReverseMatch, RegexURLPattern, RegexURLResolver, ResolverMatch, Resolver404, get_script_prefix, reverse, reverse_lazy, resolve + ) +except ImportError: + from django.core.urlresolvers import ( # Will be removed in Django 2.0 + NoReverseMatch, RegexURLPattern, RegexURLResolver, ResolverMatch, Resolver404, get_script_prefix, reverse, reverse_lazy, resolve + ) + + try: import urlparse # Python 2.x except ImportError: @@ -128,6 +138,12 @@ def is_authenticated(user): return user.is_authenticated +def is_anonymous(user): + if django.VERSION < (1, 10): + return user.is_anonymous() + return user.is_anonymous + + def get_related_model(field): if django.VERSION < (1, 9): return _resolve_model(field.rel.to) @@ -178,6 +194,13 @@ except (ImportError, SyntaxError): uritemplate = None +# requests is optional +try: + import requests +except ImportError: + requests = None + + # Django-guardian is optional. Import only if guardian is in INSTALLED_APPS # Fixes (#1712). We keep the try/except for the test suite. guardian = None @@ -200,8 +223,13 @@ try: if markdown.version <= '2.2': HEADERID_EXT_PATH = 'headerid' - else: + LEVEL_PARAM = 'level' + elif markdown.version < '2.6': HEADERID_EXT_PATH = 'markdown.extensions.headerid' + LEVEL_PARAM = 'level' + else: + HEADERID_EXT_PATH = 'markdown.extensions.toc' + LEVEL_PARAM = 'baselevel' def apply_markdown(text): """ @@ -211,7 +239,7 @@ try: extensions = [HEADERID_EXT_PATH] extension_configs = { HEADERID_EXT_PATH: { - 'level': '2' + LEVEL_PARAM: '2' } } md = markdown.Markdown( @@ -277,3 +305,11 @@ def template_render(template, context=None, request=None): # backends template, e.g. django.template.backends.django.Template else: return template.render(context, request=request) + + +def set_many(instance, field, value): + if django.VERSION < (1, 10): + setattr(instance, field, value) + else: + field = getattr(instance, field) + field.set(value) diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py index 554e5236c..bf9b32aaa 100644 --- a/rest_framework/decorators.py +++ b/rest_framework/decorators.py @@ -15,7 +15,7 @@ from django.utils import six from rest_framework.views import APIView -def api_view(http_method_names=None): +def api_view(http_method_names=None, exclude_from_schema=False): """ Decorator that converts a function-based view into an APIView subclass. Takes a list of allowed methods for the view as an argument. @@ -72,6 +72,7 @@ def api_view(http_method_names=None): WrappedAPIView.permission_classes = getattr(func, 'permission_classes', APIView.permission_classes) + WrappedAPIView.exclude_from_schema = exclude_from_schema return WrappedAPIView.as_view() return decorator diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 917a151e5..7f8391b8a 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -49,20 +49,34 @@ class empty: pass -def is_simple_callable(obj): - """ - True if the object is a callable that takes no arguments. - """ - function = inspect.isfunction(obj) - method = inspect.ismethod(obj) +if six.PY3: + def is_simple_callable(obj): + """ + True if the object is a callable that takes no arguments. + """ + if not callable(obj): + return False - if not (function or method): - return False + sig = inspect.signature(obj) + params = sig.parameters.values() + return all(param.default != param.empty for param in params) - args, _, _, defaults = inspect.getargspec(obj) - len_args = len(args) if function else len(args) - 1 - len_defaults = len(defaults) if defaults else 0 - return len_args <= len_defaults +else: + def is_simple_callable(obj): + function = inspect.isfunction(obj) + method = inspect.ismethod(obj) + + if not (function or method): + return False + + if method: + is_unbound = obj.im_self is None + + args, _, _, defaults = inspect.getargspec(obj) + + len_args = len(args) if function or is_unbound else len(args) - 1 + len_defaults = len(defaults) if defaults else 0 + return len_args <= len_defaults def get_attribute(instance, attrs): diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 0050ee7cd..65233978e 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -16,7 +16,7 @@ from django.utils import six from django.utils.translation import ugettext_lazy as _ from rest_framework.compat import ( - crispy_forms, distinct, django_filters, guardian, template_render + coreapi, crispy_forms, distinct, django_filters, guardian, template_render ) from rest_framework.settings import api_settings @@ -72,7 +72,8 @@ class BaseFilterBackend(object): """ raise NotImplementedError(".filter_queryset() must be overridden.") - def get_fields(self, view): + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' return [] @@ -131,14 +132,21 @@ class DjangoFilterBackend(BaseFilterBackend): template = loader.get_template(self.template) return template_render(template, context) - def get_fields(self, view): + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' filter_class = getattr(view, 'filter_class', None) if filter_class: - return list(filter_class().filters.keys()) + return [ + coreapi.Field(name=field_name, required=False, location='query') + for field_name in filter_class().filters.keys() + ] filter_fields = getattr(view, 'filter_fields', None) if filter_fields: - return filter_fields + return [ + coreapi.Field(name=field_name, required=False, location='query') + for field_name in filter_fields + ] return [] @@ -231,8 +239,9 @@ class SearchFilter(BaseFilterBackend): template = loader.get_template(self.template) return template_render(template, context) - def get_fields(self, view): - return [self.search_param] + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + return [coreapi.Field(name=self.search_param, required=False, location='query')] class OrderingFilter(BaseFilterBackend): @@ -348,8 +357,9 @@ class OrderingFilter(BaseFilterBackend): context = self.get_template_context(request, queryset, view) return template_render(template, context) - def get_fields(self, view): - return [self.ordering_param] + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + return [coreapi.Field(name=self.ordering_param, required=False, location='query')] class DjangoObjectPermissionsFilter(BaseFilterBackend): diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index c54894efc..6b539b900 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -15,7 +15,7 @@ from django.utils import six from django.utils.six.moves.urllib import parse as urlparse from django.utils.translation import ugettext_lazy as _ -from rest_framework.compat import template_render +from rest_framework.compat import coreapi, template_render from rest_framework.exceptions import NotFound from rest_framework.response import Response from rest_framework.settings import api_settings @@ -157,7 +157,8 @@ class BasePagination(object): def get_results(self, data): return data['results'] - def get_fields(self, view): + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' return [] @@ -283,10 +284,16 @@ class PageNumberPagination(BasePagination): context = self.get_html_context() return template_render(template, context) - def get_fields(self, view): - if self.page_size_query_param is None: - return [self.page_query_param] - return [self.page_query_param, self.page_size_query_param] + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + fields = [ + coreapi.Field(name=self.page_query_param, required=False, location='query') + ] + if self.page_size_query_param is not None: + fields.append([ + coreapi.Field(name=self.page_size_query_param, required=False, location='query') + ]) + return fields class LimitOffsetPagination(BasePagination): @@ -415,8 +422,12 @@ class LimitOffsetPagination(BasePagination): context = self.get_html_context() return template_render(template, context) - def get_fields(self, view): - return [self.limit_query_param, self.offset_query_param] + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + return [ + coreapi.Field(name=self.limit_query_param, required=False, location='query'), + coreapi.Field(name=self.offset_query_param, required=False, location='query') + ] class CursorPagination(BasePagination): @@ -721,5 +732,8 @@ class CursorPagination(BasePagination): context = self.get_html_context() return template_render(template, context) - def get_fields(self, view): - return [self.cursor_query_param] + def get_schema_fields(self, view): + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + return [ + coreapi.Field(name=self.cursor_query_param, required=False, location='query') + ] diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 65c4c0318..c6eb92a24 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -4,9 +4,6 @@ from __future__ import unicode_literals from collections import OrderedDict from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist -from django.core.urlresolvers import ( - NoReverseMatch, Resolver404, get_script_prefix, resolve -) from django.db.models import Manager from django.db.models.query import QuerySet from django.utils import six @@ -14,10 +11,14 @@ from django.utils.encoding import python_2_unicode_compatible, smart_text from django.utils.six.moves.urllib import parse as urlparse from django.utils.translation import ugettext_lazy as _ +from rest_framework.compat import ( + NoReverseMatch, Resolver404, get_script_prefix, resolve +) from rest_framework.fields import ( Field, empty, get_attribute, is_simple_callable, iter_options ) from rest_framework.reverse import reverse +from rest_framework.settings import api_settings from rest_framework.utils import html @@ -71,14 +72,19 @@ MANY_RELATION_KWARGS = ( class RelatedField(Field): queryset = None - html_cutoff = 1000 - html_cutoff_text = _('More than {count} items...') + html_cutoff = None + html_cutoff_text = None def __init__(self, **kwargs): self.queryset = kwargs.pop('queryset', self.queryset) - self.html_cutoff = kwargs.pop('html_cutoff', self.html_cutoff) - self.html_cutoff_text = kwargs.pop('html_cutoff_text', self.html_cutoff_text) - + self.html_cutoff = kwargs.pop( + 'html_cutoff', + self.html_cutoff or int(api_settings.HTML_SELECT_CUTOFF) + ) + self.html_cutoff_text = kwargs.pop( + 'html_cutoff_text', + self.html_cutoff_text or _(api_settings.HTML_SELECT_CUTOFF_TEXT) + ) if not method_overridden('get_queryset', RelatedField, self): assert self.queryset is not None or kwargs.get('read_only', None), ( 'Relational field must provide a `queryset` argument, ' @@ -447,15 +453,20 @@ class ManyRelatedField(Field): 'not_a_list': _('Expected a list of items but got type "{input_type}".'), 'empty': _('This list may not be empty.') } - html_cutoff = 1000 - html_cutoff_text = _('More than {count} items...') + html_cutoff = None + html_cutoff_text = None def __init__(self, child_relation=None, *args, **kwargs): self.child_relation = child_relation self.allow_empty = kwargs.pop('allow_empty', True) - self.html_cutoff = kwargs.pop('html_cutoff', self.html_cutoff) - self.html_cutoff_text = kwargs.pop('html_cutoff_text', self.html_cutoff_text) - + self.html_cutoff = kwargs.pop( + 'html_cutoff', + self.html_cutoff or int(api_settings.HTML_SELECT_CUTOFF) + ) + self.html_cutoff_text = kwargs.pop( + 'html_cutoff_text', + self.html_cutoff_text or _(api_settings.HTML_SELECT_CUTOFF_TEXT) + ) assert child_relation is not None, '`child_relation` is a required argument.' super(ManyRelatedField, self).__init__(*args, **kwargs) self.child_relation.bind(field_name='', parent=self) diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 11e9fb960..97984daf9 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -276,6 +276,10 @@ class HTMLFormRenderer(BaseRenderer): 'base_template': 'input.html', 'input_type': 'number' }, + serializers.FloatField: { + 'base_template': 'input.html', + 'input_type': 'number' + }, serializers.DateTimeField: { 'base_template': 'input.html', 'input_type': 'datetime-local' @@ -809,7 +813,7 @@ class MultiPartRenderer(BaseRenderer): class CoreJSONRenderer(BaseRenderer): - media_type = 'application/vnd.coreapi+json' + media_type = 'application/coreapi+json' charset = None format = 'corejson' diff --git a/rest_framework/reverse.py b/rest_framework/reverse.py index 5a7ba09a8..fd418dcca 100644 --- a/rest_framework/reverse.py +++ b/rest_framework/reverse.py @@ -3,11 +3,11 @@ 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 -from django.core.urlresolvers import NoReverseMatch from django.utils import six from django.utils.functional import lazy +from rest_framework.compat import reverse as django_reverse +from rest_framework.compat import NoReverseMatch from rest_framework.settings import api_settings from rest_framework.utils.urls import replace_query_param @@ -54,7 +54,7 @@ def reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra def _reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra): """ - Same as `django.core.urlresolvers.reverse`, but optionally takes a request + Same as `django.urls.reverse`, but optionally takes a request and returns a fully qualified URL, using the request to get the base URL. """ if format is not None: diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 4eec70bda..7a2d981a3 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -16,13 +16,15 @@ For example, you might have a `urls.py` that looks something like this: from __future__ import unicode_literals import itertools +import warnings from collections import OrderedDict, namedtuple from django.conf.urls import url from django.core.exceptions import ImproperlyConfigured -from django.core.urlresolvers import NoReverseMatch from rest_framework import exceptions, renderers, views +from rest_framework.compat import NoReverseMatch +from rest_framework.renderers import BrowsableAPIRenderer from rest_framework.response import Response from rest_framework.reverse import reverse from rest_framework.schemas import SchemaGenerator @@ -83,6 +85,7 @@ class BaseRouter(object): class SimpleRouter(BaseRouter): + routes = [ # List route. Route( @@ -258,6 +261,13 @@ class SimpleRouter(BaseRouter): trailing_slash=self.trailing_slash ) + # If there is no prefix, the first part of the url is probably + # controlled by project's urls.py and the router is in an app, + # so a slash in the beginning will (A) cause Django to give + # warnings and (B) generate URLS that will require using '//'. + if not prefix and regex[:2] == '^/': + regex = '^' + regex[2:] + view = viewset.as_view(mapping, **route.initkwargs) name = route.name.format(basename=basename) ret.append(url(regex, view, name=name)) @@ -273,9 +283,15 @@ class DefaultRouter(SimpleRouter): include_root_view = True include_format_suffixes = True root_view_name = 'api-root' - default_schema_renderers = [renderers.CoreJSONRenderer] + default_schema_renderers = [renderers.CoreJSONRenderer, BrowsableAPIRenderer] def __init__(self, *args, **kwargs): + if 'schema_title' in kwargs: + warnings.warn( + "Including a schema directly via a router is now pending " + "deprecation. Use `get_schema_view()` instead.", + PendingDeprecationWarning + ) if 'schema_renderers' in kwargs: assert 'schema_title' in kwargs, 'Missing "schema_title" argument.' if 'schema_url' in kwargs: @@ -289,42 +305,44 @@ class DefaultRouter(SimpleRouter): self.root_renderers = list(api_settings.DEFAULT_RENDERER_CLASSES) super(DefaultRouter, self).__init__(*args, **kwargs) + def get_schema_root_view(self, api_urls=None): + """ + Return a schema root view. + """ + schema_renderers = self.schema_renderers + schema_generator = SchemaGenerator( + title=self.schema_title, + url=self.schema_url, + patterns=api_urls + ) + + class APISchemaView(views.APIView): + _ignore_model_permissions = True + exclude_from_schema = True + renderer_classes = schema_renderers + + def get(self, request, *args, **kwargs): + schema = schema_generator.get_schema(request) + if schema is None: + raise exceptions.PermissionDenied() + return Response(schema) + + return APISchemaView.as_view() + def get_api_root_view(self, api_urls=None): """ - Return a view to use as the API root. + Return a basic root view. """ api_root_dict = OrderedDict() list_name = self.routes[0].name for prefix, viewset, basename in self.registry: api_root_dict[prefix] = list_name.format(basename=basename) - view_renderers = list(self.root_renderers) - schema_media_types = [] - - if api_urls and self.schema_title: - view_renderers += list(self.schema_renderers) - schema_generator = SchemaGenerator( - title=self.schema_title, - url=self.schema_url, - patterns=api_urls - ) - schema_media_types = [ - renderer.media_type - for renderer in self.schema_renderers - ] - - class APIRoot(views.APIView): + class APIRootView(views.APIView): _ignore_model_permissions = True - renderer_classes = view_renderers + exclude_from_schema = True def get(self, request, *args, **kwargs): - if request.accepted_renderer.media_type in schema_media_types: - # Return a schema response. - schema = schema_generator.get_schema(request) - if schema is None: - raise exceptions.PermissionDenied() - return Response(schema) - # Return a plain {"name": "hyperlink"} response. ret = OrderedDict() namespace = request.resolver_match.namespace @@ -345,7 +363,7 @@ class DefaultRouter(SimpleRouter): return Response(ret) - return APIRoot.as_view() + return APIRootView.as_view() def get_urls(self): """ @@ -355,7 +373,10 @@ class DefaultRouter(SimpleRouter): urls = super(DefaultRouter, self).get_urls() if self.include_root_view: - view = self.get_api_root_view(api_urls=urls) + if self.schema_title: + view = self.get_schema_root_view(api_urls=urls) + else: + view = self.get_api_root_view(api_urls=urls) root_url = url(r'^$', view, name=self.root_view_name) urls.append(root_url) diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py index 1b899450f..69698db0e 100644 --- a/rest_framework/schemas.py +++ b/rest_framework/schemas.py @@ -1,26 +1,45 @@ +import os +import re +from collections import OrderedDict from importlib import import_module from django.conf import settings from django.contrib.admindocs.views import simplify_regex -from django.core.urlresolvers import RegexURLPattern, RegexURLResolver from django.utils import six -from django.utils.encoding import force_text +from django.utils.encoding import force_text, smart_text -from rest_framework import exceptions, serializers -from rest_framework.compat import coreapi, uritemplate, urlparse +from rest_framework import exceptions, renderers, serializers +from rest_framework.compat import ( + RegexURLPattern, RegexURLResolver, coreapi, uritemplate, urlparse +) from rest_framework.request import clone_request +from rest_framework.response import Response +from rest_framework.settings import api_settings +from rest_framework.utils import formatting +from rest_framework.utils.field_mapping import ClassLookupDict +from rest_framework.utils.model_meta import _get_pk from rest_framework.views import APIView -def as_query_fields(items): - """ - Take a list of Fields and plain strings. - Convert any pain strings into `location='query'` Field instances. - """ - return [ - item if isinstance(item, coreapi.Field) else coreapi.Field(name=item, required=False, location='query') - for item in items - ] +header_regex = re.compile('^[a-zA-Z][0-9A-Za-z_]*:') + +types_lookup = ClassLookupDict({ + serializers.Field: 'string', + serializers.IntegerField: 'integer', + serializers.FloatField: 'number', + serializers.DecimalField: 'number', + serializers.BooleanField: 'boolean', + serializers.FileField: 'file', + serializers.MultipleChoiceField: 'array', + serializers.ManyRelatedField: 'array', + serializers.Serializer: 'object', + serializers.ListSerializer: 'array' +}) + + +def get_pk_name(model): + meta = model._meta.concrete_model._meta + return _get_pk(meta).name def is_api_view(callback): @@ -31,106 +50,92 @@ def is_api_view(callback): return (cls is not None) and issubclass(cls, APIView) -class SchemaGenerator(object): - default_mapping = { - 'get': 'read', - 'post': 'create', - 'put': 'update', - 'patch': 'partial_update', - 'delete': 'destroy', - } - known_actions = ( - 'create', 'read', 'retrieve', 'list', - 'update', 'partial_update', 'destroy' - ) +def insert_into(target, keys, value): + """ + Nested dictionary insertion. - def __init__(self, title=None, url=None, patterns=None, urlconf=None): - assert coreapi, '`coreapi` must be installed for schema support.' + >>> example = {} + >>> insert_into(example, ['a', 'b', 'c'], 123) + >>> example + {'a': {'b': {'c': 123}}} + """ + for key in keys[:-1]: + if key not in target: + target[key] = {} + target = target[key] + target[keys[-1]] = value - if patterns is None and urlconf is not None: + +def is_custom_action(action): + return action not in set([ + 'retrieve', 'list', 'create', 'update', 'partial_update', 'destroy' + ]) + + +def is_list_view(path, method, view): + """ + Return True if the given path/method appears to represent a list view. + """ + if hasattr(view, 'action'): + # Viewsets have an explicitly defined action, which we can inspect. + return view.action == 'list' + + if method.lower() != 'get': + return False + path_components = path.strip('/').split('/') + if path_components and '{' in path_components[-1]: + return False + return True + + +def endpoint_ordering(endpoint): + path, method, callback = endpoint + method_priority = { + 'GET': 0, + 'POST': 1, + 'PUT': 2, + 'PATCH': 3, + 'DELETE': 4 + }.get(method, 5) + return (path, method_priority) + + +class EndpointInspector(object): + """ + A class to determine the available API endpoints that a project exposes. + """ + def __init__(self, patterns=None, urlconf=None): + if patterns is None: + if urlconf is None: + # Use the default Django URL conf + urlconf = settings.ROOT_URLCONF + + # Load the given URLconf module if isinstance(urlconf, six.string_types): urls = import_module(urlconf) else: urls = urlconf - self.patterns = urls.urlpatterns - elif patterns is None and urlconf is None: - urls = import_module(settings.ROOT_URLCONF) - self.patterns = urls.urlpatterns - else: - self.patterns = patterns + patterns = urls.urlpatterns - if url and not url.endswith('/'): - url += '/' + self.patterns = patterns - self.title = title - self.url = url - self.endpoints = None - - def get_schema(self, request=None): - if self.endpoints is None: - self.endpoints = self.get_api_endpoints(self.patterns) - - links = [] - for path, method, category, action, callback in self.endpoints: - view = callback.cls() - for attr, val in getattr(callback, 'initkwargs', {}).items(): - setattr(view, attr, val) - view.args = () - view.kwargs = {} - view.format_kwarg = None - - actions = getattr(callback, 'actions', None) - if actions is not None: - if method == 'OPTIONS': - view.action = 'metadata' - else: - view.action = actions.get(method.lower()) - - if request is not None: - view.request = clone_request(request, method) - try: - view.check_permissions(view.request) - except exceptions.APIException: - continue - else: - view.request = None - - link = self.get_link(path, method, callback, view) - links.append((category, action, link)) - - if not links: - return None - - # Generate the schema content structure, eg: - # {'users': {'list': Link()}} - content = {} - for category, action, link in links: - if category is None: - content[action] = link - elif category in content: - content[category][action] = link - else: - content[category] = {action: link} - - # Return the schema document. - return coreapi.Document(title=self.title, content=content, url=self.url) - - def get_api_endpoints(self, patterns, prefix=''): + def get_api_endpoints(self, patterns=None, prefix=''): """ Return a list of all available API endpoints by inspecting the URL conf. """ + if patterns is None: + patterns = self.patterns + api_endpoints = [] for pattern in patterns: path_regex = prefix + pattern.regex.pattern if isinstance(pattern, RegexURLPattern): - path = self.get_path(path_regex) + path = self.get_path_from_regex(path_regex) callback = pattern.callback if self.should_include_endpoint(path, callback): for method in self.get_allowed_methods(callback): - action = self.get_action(path, method, callback) - category = self.get_category(path, method, callback, action) - endpoint = (path, method, category, action, callback) + endpoint = (path, method, callback) api_endpoints.append(endpoint) elif isinstance(pattern, RegexURLResolver): @@ -140,9 +145,11 @@ class SchemaGenerator(object): ) api_endpoints.extend(nested_endpoints) + api_endpoints = sorted(api_endpoints, key=endpoint_ordering) + return api_endpoints - def get_path(self, path_regex): + def get_path_from_regex(self, path_regex): """ Given a URL conf regex, return a URI template string. """ @@ -160,9 +167,6 @@ class SchemaGenerator(object): if path.endswith('.{format}') or path.endswith('.{format}/'): return False # Ignore .json style URLs. - if path == '/': - return False # Ignore the root endpoint. - return True def get_allowed_methods(self, callback): @@ -177,60 +181,190 @@ class SchemaGenerator(object): callback.cls().allowed_methods if method not in ('OPTIONS', 'HEAD') ] - def get_action(self, path, method, callback): - """ - Return a descriptive action string for the endpoint, eg. 'list'. - """ - actions = getattr(callback, 'actions', self.default_mapping) - return actions[method.lower()] - def get_category(self, path, method, callback, action): - """ - Return a descriptive category string for the endpoint, eg. 'users'. +class SchemaGenerator(object): + # Map HTTP methods onto actions. + default_mapping = { + 'get': 'retrieve', + 'post': 'create', + 'put': 'update', + 'patch': 'partial_update', + 'delete': 'destroy', + } + endpoint_inspector_cls = EndpointInspector - Examples of category/action pairs that should be generated for various - endpoints: + # Map the method names we use for viewset actions onto external schema names. + # These give us names that are more suitable for the external representation. + # Set by 'SCHEMA_COERCE_METHOD_NAMES'. + coerce_method_names = None - /users/ [users][list], [users][create] - /users/{pk}/ [users][read], [users][update], [users][destroy] - /users/enabled/ [users][enabled] (custom action) - /users/{pk}/star/ [users][star] (custom action) - /users/{pk}/groups/ [groups][list], [groups][create] - /users/{pk}/groups/{pk}/ [groups][read], [groups][update], [groups][destroy] + # 'pk' isn't great as an externally exposed name for an identifier, + # so by default we prefer to use the actual model field name for schemas. + # Set by 'SCHEMA_COERCE_PATH_PK'. + coerce_path_pk = None + + def __init__(self, title=None, url=None, patterns=None, urlconf=None): + assert coreapi, '`coreapi` must be installed for schema support.' + + if url and not url.endswith('/'): + url += '/' + + self.coerce_method_names = api_settings.SCHEMA_COERCE_METHOD_NAMES + self.coerce_path_pk = api_settings.SCHEMA_COERCE_PATH_PK + + self.patterns = patterns + self.urlconf = urlconf + self.title = title + self.url = url + self.endpoints = None + + def get_schema(self, request=None): """ - path_components = path.strip('/').split('/') - path_components = [ - component for component in path_components - if '{' not in component - ] - if action in self.known_actions: - # Default action, eg "/users/", "/users/{pk}/" - idx = -1 - else: - # Custom action, eg "/users/{pk}/activate/", "/users/active/" - idx = -2 + Generate a `coreapi.Document` representing the API schema. + """ + if self.endpoints is None: + inspector = self.endpoint_inspector_cls(self.patterns, self.urlconf) + self.endpoints = inspector.get_api_endpoints() + + links = self.get_links(request) + if not links: + return None + return coreapi.Document(title=self.title, url=self.url, content=links) + + def get_links(self, request=None): + """ + Return a dictionary containing all the links that should be + included in the API schema. + """ + links = OrderedDict() + + # Generate (path, method, view) given (path, method, callback). + paths = [] + view_endpoints = [] + for path, method, callback in self.endpoints: + view = self.create_view(callback, method, request) + if getattr(view, 'exclude_from_schema', False): + continue + path = self.coerce_path(path, method, view) + paths.append(path) + view_endpoints.append((path, method, view)) + + # Only generate the path prefix for paths that will be included + prefix = self.determine_path_prefix(paths) + + for path, method, view in view_endpoints: + if not self.has_view_permissions(path, method, view): + continue + link = self.get_link(path, method, view) + subpath = path[len(prefix):] + keys = self.get_keys(subpath, method, view) + insert_into(links, keys, link) + return links + + # Methods used when we generate a view instance from the raw callback... + + def determine_path_prefix(self, paths): + """ + Given a list of all paths, return the common prefix which should be + discounted when generating a schema structure. + + This will be the longest common string that does not include that last + component of the URL, or the last component before a path parameter. + + For example: + + /api/v1/users/ + /api/v1/users/{pk}/ + + The path prefix is '/api/v1/' + """ + prefixes = [] + for path in paths: + components = path.strip('/').split('/') + initial_components = [] + for component in components: + if '{' in component: + break + initial_components.append(component) + prefix = '/'.join(initial_components[:-1]) + if not prefix: + # We can just break early in the case that there's at least + # one URL that doesn't have a path prefix. + return '/' + prefixes.append('/' + prefix + '/') + return os.path.commonprefix(prefixes) + + def create_view(self, callback, method, request=None): + """ + Given a callback, return an actual view instance. + """ + view = callback.cls() + for attr, val in getattr(callback, 'initkwargs', {}).items(): + setattr(view, attr, val) + view.args = () + view.kwargs = {} + view.format_kwarg = None + view.request = None + view.action_map = getattr(callback, 'actions', None) + + actions = getattr(callback, 'actions', None) + if actions is not None: + if method == 'OPTIONS': + view.action = 'metadata' + else: + view.action = actions.get(method.lower()) + + if request is not None: + view.request = clone_request(request, method) + + return view + + def has_view_permissions(self, path, method, view): + """ + Return `True` if the incoming request has the correct view permissions. + """ + if view.request is None: + return True try: - return path_components[idx] - except IndexError: - return None + view.check_permissions(view.request) + except exceptions.APIException: + return False + return True + + def coerce_path(self, path, method, view): + """ + Coerce {pk} path arguments into the name of the model field, + where possible. This is cleaner for an external representation. + (Ie. "this is an identifier", not "this is a database primary key") + """ + if not self.coerce_path_pk or '{pk}' not in path: + return path + model = getattr(getattr(view, 'queryset', None), 'model', None) + if model: + field_name = get_pk_name(model) + else: + field_name = 'id' + return path.replace('{pk}', '{%s}' % field_name) # Methods for generating each individual `Link` instance... - def get_link(self, path, method, callback, view): + def get_link(self, path, method, view): """ Return a `coreapi.Link` instance for the given endpoint. """ - fields = self.get_path_fields(path, method, callback, view) - fields += self.get_serializer_fields(path, method, callback, view) - fields += self.get_pagination_fields(path, method, callback, view) - fields += self.get_filter_fields(path, method, callback, view) + fields = self.get_path_fields(path, method, view) + fields += self.get_serializer_fields(path, method, view) + fields += self.get_pagination_fields(path, method, view) + fields += self.get_filter_fields(path, method, view) if fields and any([field.location in ('form', 'body') for field in fields]): - encoding = self.get_encoding(path, method, callback, view) + encoding = self.get_encoding(path, method, view) else: encoding = None + description = self.get_description(path, method, view) + if self.url and path.startswith('/'): path = path[1:] @@ -238,10 +372,44 @@ class SchemaGenerator(object): url=urlparse.urljoin(self.url, path), action=method.lower(), encoding=encoding, - fields=fields + fields=fields, + description=description ) - def get_encoding(self, path, method, callback, view): + def get_description(self, path, method, view): + """ + Determine a link description. + + This will be based on the method docstring if one exists, + or else the class docstring. + """ + method_name = getattr(view, 'action', method.lower()) + method_docstring = getattr(view, method_name, None).__doc__ + if method_docstring: + # An explicit docstring on the method or action. + return formatting.dedent(smart_text(method_docstring)) + + description = view.get_view_description() + lines = [line.strip() for line in description.splitlines()] + current_section = '' + sections = {'': ''} + + for line in lines: + if header_regex.match(line): + current_section, seperator, lead = line.partition(':') + sections[current_section] = lead.strip() + else: + sections[current_section] += line + '\n' + + header = getattr(view, 'action', method.lower()) + if header in sections: + return sections[header].strip() + if header in self.coerce_method_names: + if self.coerce_method_names[header] in sections: + return sections[self.coerce_method_names[header]].strip() + return sections[''].strip() + + def get_encoding(self, path, method, view): """ Return the 'encoding' parameter to use for a given endpoint. """ @@ -262,7 +430,7 @@ class SchemaGenerator(object): return None - def get_path_fields(self, path, method, callback, view): + def get_path_fields(self, path, method, view): """ Return a list of `coreapi.Field` instances corresponding to any templated path variables. @@ -275,7 +443,7 @@ class SchemaGenerator(object): return fields - def get_serializer_fields(self, path, method, callback, view): + def get_serializer_fields(self, path, method, view): """ Return a list of `coreapi.Field` instances corresponding to any request body input, as determined by the serializer class. @@ -289,7 +457,14 @@ class SchemaGenerator(object): serializer = view.get_serializer() if isinstance(serializer, serializers.ListSerializer): - return [coreapi.Field(name='data', location='body', required=True)] + return [ + coreapi.Field( + name='data', + location='body', + required=True, + type='array' + ) + ] if not isinstance(serializer, serializers.Serializer): return [] @@ -305,36 +480,104 @@ class SchemaGenerator(object): name=field.source, location='form', required=required, - description=description + description=description, + type=types_lookup[field] ) fields.append(field) return fields - def get_pagination_fields(self, path, method, callback, view): - if method != 'GET': - return [] - - if hasattr(callback, 'actions') and ('list' not in callback.actions.values()): + def get_pagination_fields(self, path, method, view): + if not is_list_view(path, method, view): return [] if not getattr(view, 'pagination_class', None): return [] paginator = view.pagination_class() - return as_query_fields(paginator.get_fields(view)) + return paginator.get_schema_fields(view) - def get_filter_fields(self, path, method, callback, view): - if method != 'GET': + def get_filter_fields(self, path, method, view): + if not is_list_view(path, method, view): return [] - if hasattr(callback, 'actions') and ('list' not in callback.actions.values()): - return [] - - if not hasattr(view, 'filter_backends'): + if not getattr(view, 'filter_backends', None): return [] fields = [] for filter_backend in view.filter_backends: - fields += as_query_fields(filter_backend().get_fields(view)) + fields += filter_backend().get_schema_fields(view) return fields + + # Method for generating the link layout.... + + def get_keys(self, subpath, method, view): + """ + Return a list of keys that should be used to layout a link within + the schema document. + + /users/ ("users", "list"), ("users", "create") + /users/{pk}/ ("users", "read"), ("users", "update"), ("users", "delete") + /users/enabled/ ("users", "enabled") # custom viewset list action + /users/{pk}/star/ ("users", "star") # custom viewset detail action + /users/{pk}/groups/ ("users", "groups", "list"), ("users", "groups", "create") + /users/{pk}/groups/{pk}/ ("users", "groups", "read"), ("users", "groups", "update"), ("users", "groups", "delete") + """ + if hasattr(view, 'action'): + # Viewsets have explicitly named actions. + action = view.action + else: + # Views have no associated action, so we determine one from the method. + if is_list_view(subpath, method, view): + action = 'list' + else: + action = self.default_mapping[method.lower()] + + named_path_components = [ + component for component + in subpath.strip('/').split('/') + if '{' not in component + ] + + if is_custom_action(action): + # Custom action, eg "/users/{pk}/activate/", "/users/active/" + if len(view.action_map) > 1: + action = self.default_mapping[method.lower()] + if action in self.coerce_method_names: + action = self.coerce_method_names[action] + return named_path_components + [action] + else: + return named_path_components[:-1] + [action] + + if action in self.coerce_method_names: + action = self.coerce_method_names[action] + + # Default action, eg "/users/", "/users/{pk}/" + return named_path_components + [action] + + +def get_schema_view(title=None, url=None, renderer_classes=None): + """ + Return a schema view. + """ + generator = SchemaGenerator(title=title, url=url) + if renderer_classes is None: + if renderers.BrowsableAPIRenderer in api_settings.DEFAULT_RENDERER_CLASSES: + rclasses = [renderers.CoreJSONRenderer, renderers.BrowsableAPIRenderer] + else: + rclasses = [renderers.CoreJSONRenderer] + else: + rclasses = renderer_classes + + class SchemaView(APIView): + _ignore_model_permissions = True + exclude_from_schema = True + renderer_classes = rclasses + + def get(self, request, *args, **kwargs): + schema = generator.get_schema(request) + if schema is None: + raise exceptions.PermissionDenied() + return Response(schema) + + return SchemaView.as_view() diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 4d1ed63ae..7e99d40b3 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -13,7 +13,6 @@ response content is handled by parsers and renderers. from __future__ import unicode_literals import traceback -import warnings from django.db import models from django.db.models import DurationField as ModelDurationField @@ -23,7 +22,7 @@ from django.utils.functional import cached_property from django.utils.translation import ugettext_lazy as _ from rest_framework.compat import JSONField as ModelJSONField -from rest_framework.compat import postgres_fields, unicode_to_repr +from rest_framework.compat import postgres_fields, set_many, unicode_to_repr from rest_framework.utils import model_meta from rest_framework.utils.field_mapping import ( ClassLookupDict, get_field_kwargs, get_nested_relation_kwargs, @@ -892,19 +891,23 @@ class ModelSerializer(Serializer): # Save many-to-many relationships after the instance is created. if many_to_many: for field_name, value in many_to_many.items(): - setattr(instance, field_name, value) + set_many(instance, field_name, value) return instance def update(self, instance, validated_data): raise_errors_on_nested_writes('update', self, validated_data) + info = model_meta.get_field_info(instance) # Simply set each attribute on the instance, and then save it. # Note that unlike `.create()` we don't need to treat many-to-many # relationships as being a special case. During updates we already # have an instance pk for the relationships to be associated with. for attr, value in validated_data.items(): - setattr(instance, attr, value) + if attr in info.relations and info.relations[attr].to_many: + set_many(instance, attr, value) + else: + setattr(instance, attr, value) instance.save() return instance @@ -1012,16 +1015,14 @@ class ModelSerializer(Serializer): ) ) - if fields is None and exclude is None: - warnings.warn( - "Creating a ModelSerializer without either the 'fields' " - "attribute or the 'exclude' attribute is deprecated " - "since 3.3.0. Add an explicit fields = '__all__' to the " - "{serializer_class} serializer.".format( - serializer_class=self.__class__.__name__ - ), - DeprecationWarning - ) + assert not (fields is None and exclude is None), ( + "Creating a ModelSerializer without either the 'fields' attribute " + "or the 'exclude' attribute has been deprecated since 3.3.0, " + "and is now disallowed. Add an explicit fields = '__all__' to the " + "{serializer_class} serializer.".format( + serializer_class=self.__class__.__name__ + ), + ) if fields == ALL_FIELDS: fields = None diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 68c7709e8..6d9ed2355 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -111,6 +111,17 @@ DEFAULTS = { 'COMPACT_JSON': True, 'COERCE_DECIMAL_TO_STRING': True, 'UPLOADED_FILES_USE_URL': True, + + # Browseable API + 'HTML_SELECT_CUTOFF': 1000, + 'HTML_SELECT_CUTOFF_TEXT': "More than {count} items...", + + # Schemas + 'SCHEMA_COERCE_PATH_PK': True, + 'SCHEMA_COERCE_METHOD_NAMES': { + 'retrieve': 'read', + 'destroy': 'delete' + }, } diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index 3bb85e472..c1c8a5396 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -3,14 +3,13 @@ from __future__ import absolute_import, unicode_literals import re from django import template -from django.core.urlresolvers import NoReverseMatch, reverse from django.template import loader from django.utils import six from django.utils.encoding import force_text, iri_to_uri from django.utils.html import escape, format_html, smart_urlquote from django.utils.safestring import SafeData, mark_safe -from rest_framework.compat import template_render +from rest_framework.compat import NoReverseMatch, reverse, template_render from rest_framework.renderers import HTMLFormRenderer from rest_framework.utils.urls import replace_query_param diff --git a/rest_framework/test.py b/rest_framework/test.py index fd9f6ab13..16b1b4cd5 100644 --- a/rest_framework/test.py +++ b/rest_framework/test.py @@ -4,7 +4,11 @@ # to make it harder for the user to import the wrong thing without realizing. from __future__ import unicode_literals +import io + from django.conf import settings +from django.core.exceptions import ImproperlyConfigured +from django.core.handlers.wsgi import WSGIHandler from django.test import testcases from django.test.client import Client as DjangoClient from django.test.client import RequestFactory as DjangoRequestFactory @@ -13,6 +17,7 @@ from django.utils import six from django.utils.encoding import force_bytes from django.utils.http import urlencode +from rest_framework.compat import coreapi, requests from rest_framework.settings import api_settings @@ -21,6 +26,128 @@ def force_authenticate(request, user=None, token=None): request._force_auth_token = token +if requests is not None: + class HeaderDict(requests.packages.urllib3._collections.HTTPHeaderDict): + def get_all(self, key, default): + return self.getheaders(key) + + class MockOriginalResponse(object): + def __init__(self, headers): + self.msg = HeaderDict(headers) + self.closed = False + + def isclosed(self): + return self.closed + + def close(self): + self.closed = True + + class DjangoTestAdapter(requests.adapters.HTTPAdapter): + """ + A transport adapter for `requests`, that makes requests via the + Django WSGI app, rather than making actual HTTP requests over the network. + """ + def __init__(self): + self.app = WSGIHandler() + self.factory = DjangoRequestFactory() + + def get_environ(self, request): + """ + Given a `requests.PreparedRequest` instance, return a WSGI environ dict. + """ + method = request.method + url = request.url + kwargs = {} + + # Set request content, if any exists. + if request.body is not None: + if hasattr(request.body, 'read'): + kwargs['data'] = request.body.read() + else: + kwargs['data'] = request.body + if 'content-type' in request.headers: + kwargs['content_type'] = request.headers['content-type'] + + # Set request headers. + for key, value in request.headers.items(): + key = key.upper() + if key in ('CONNECTION', 'CONTENT-LENGTH', 'CONTENT-TYPE'): + continue + kwargs['HTTP_%s' % key.replace('-', '_')] = value + + return self.factory.generic(method, url, **kwargs).environ + + def send(self, request, *args, **kwargs): + """ + Make an outgoing request to the Django WSGI application. + """ + raw_kwargs = {} + + def start_response(wsgi_status, wsgi_headers): + status, _, reason = wsgi_status.partition(' ') + raw_kwargs['status'] = int(status) + raw_kwargs['reason'] = reason + raw_kwargs['headers'] = wsgi_headers + raw_kwargs['version'] = 11 + raw_kwargs['preload_content'] = False + raw_kwargs['original_response'] = MockOriginalResponse(wsgi_headers) + + # Make the outgoing request via WSGI. + environ = self.get_environ(request) + wsgi_response = self.app(environ, start_response) + + # Build the underlying urllib3.HTTPResponse + raw_kwargs['body'] = io.BytesIO(b''.join(wsgi_response)) + raw = requests.packages.urllib3.HTTPResponse(**raw_kwargs) + + # Build the requests.Response + return self.build_response(request, raw) + + def close(self): + pass + + class NoExternalRequestsAdapter(requests.adapters.HTTPAdapter): + def send(self, request, *args, **kwargs): + msg = ( + 'RequestsClient refusing to make an outgoing network request ' + 'to "%s". Only "testserver" or hostnames in your ALLOWED_HOSTS ' + 'setting are valid.' % request.url + ) + raise RuntimeError(msg) + + class RequestsClient(requests.Session): + def __init__(self, *args, **kwargs): + super(RequestsClient, self).__init__(*args, **kwargs) + adapter = DjangoTestAdapter() + self.mount('http://', adapter) + self.mount('https://', adapter) + + def request(self, method, url, *args, **kwargs): + if ':' not in url: + raise ValueError('Missing "http:" or "https:". Use a fully qualified URL, eg "http://testserver%s"' % url) + return super(RequestsClient, self).request(method, url, *args, **kwargs) + +else: + def RequestsClient(*args, **kwargs): + raise ImproperlyConfigured('requests must be installed in order to use RequestsClient.') + + +if coreapi is not None: + class CoreAPIClient(coreapi.Client): + def __init__(self, *args, **kwargs): + self._session = RequestsClient() + kwargs['transports'] = [coreapi.transports.HTTPTransport(session=self.session)] + return super(CoreAPIClient, self).__init__(*args, **kwargs) + + @property + def session(self): + return self._session + +else: + def CoreAPIClient(*args, **kwargs): + raise ImproperlyConfigured('coreapi must be installed in order to use CoreAPIClient.') + + class APIRequestFactory(DjangoRequestFactory): renderer_classes_list = api_settings.TEST_REQUEST_RENDERER_CLASSES default_format = api_settings.TEST_REQUEST_DEFAULT_FORMAT diff --git a/rest_framework/urlpatterns.py b/rest_framework/urlpatterns.py index 7a02bb0f0..4ea55300e 100644 --- a/rest_framework/urlpatterns.py +++ b/rest_framework/urlpatterns.py @@ -1,8 +1,8 @@ from __future__ import unicode_literals from django.conf.urls import include, url -from django.core.urlresolvers import RegexURLResolver +from rest_framework.compat import RegexURLResolver from rest_framework.settings import api_settings diff --git a/rest_framework/utils/breadcrumbs.py b/rest_framework/utils/breadcrumbs.py index 2e3ab9084..74f4f7840 100644 --- a/rest_framework/utils/breadcrumbs.py +++ b/rest_framework/utils/breadcrumbs.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals -from django.core.urlresolvers import get_script_prefix, resolve +from rest_framework.compat import get_script_prefix, resolve def get_breadcrumbs(url, request=None): diff --git a/rest_framework/views.py b/rest_framework/views.py index e178a209f..a8710c7a0 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -110,6 +110,9 @@ class APIView(View): # Allow dependency injection of other settings to make testing easier. settings = api_settings + # Mark the view as being included or excluded from schema generation. + exclude_from_schema = False + @classmethod def as_view(cls, **initkwargs): """ @@ -129,6 +132,7 @@ class APIView(View): view = super(APIView, cls).as_view(**initkwargs) view.cls = cls + view.initkwargs = initkwargs # Note: session based authentication is explicitly CSRF validated, # all other authentication is CSRF exempt. diff --git a/tests/browsable_api/test_form_rendering.py b/tests/browsable_api/test_form_rendering.py index 5a31ae0dd..8b79ab6ff 100644 --- a/tests/browsable_api/test_form_rendering.py +++ b/tests/browsable_api/test_form_rendering.py @@ -11,6 +11,7 @@ factory = APIRequestFactory() class BasicSerializer(serializers.ModelSerializer): class Meta: model = BasicModel + fields = '__all__' class ManyPostView(generics.GenericAPIView): diff --git a/tests/conftest.py b/tests/conftest.py index a5123b9d8..256678226 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,13 @@ def pytest_configure(): from django.conf import settings + MIDDLEWARE = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + ) + settings.configure( DEBUG_PROPAGATE_EXCEPTIONS=True, DATABASES={ @@ -21,12 +28,8 @@ def pytest_configure(): 'APP_DIRS': True, }, ], - MIDDLEWARE_CLASSES=( - 'django.middleware.common.CommonMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - ), + MIDDLEWARE=MIDDLEWARE, + MIDDLEWARE_CLASSES=MIDDLEWARE, INSTALLED_APPS=( 'django.contrib.auth', 'django.contrib.contenttypes', diff --git a/tests/test_api_client.py b/tests/test_api_client.py new file mode 100644 index 000000000..a6d72357a --- /dev/null +++ b/tests/test_api_client.py @@ -0,0 +1,474 @@ +from __future__ import unicode_literals + +import os +import tempfile +import unittest + +from django.conf.urls import url +from django.http import HttpResponse +from django.test import override_settings + +from rest_framework.compat import coreapi +from rest_framework.parsers import FileUploadParser +from rest_framework.renderers import CoreJSONRenderer +from rest_framework.response import Response +from rest_framework.test import APITestCase, CoreAPIClient +from rest_framework.views import APIView + + +def get_schema(): + return coreapi.Document( + url='https://api.example.com/', + title='Example API', + content={ + 'simple_link': coreapi.Link('/example/', description='example link'), + 'headers': coreapi.Link('/headers/'), + 'location': { + 'query': coreapi.Link('/example/', fields=[ + coreapi.Field(name='example', description='example field') + ]), + 'form': coreapi.Link('/example/', action='post', fields=[ + coreapi.Field(name='example'), + ]), + 'body': coreapi.Link('/example/', action='post', fields=[ + coreapi.Field(name='example', location='body') + ]), + 'path': coreapi.Link('/example/{id}', fields=[ + coreapi.Field(name='id', location='path') + ]) + }, + 'encoding': { + 'multipart': coreapi.Link('/example/', action='post', encoding='multipart/form-data', fields=[ + coreapi.Field(name='example') + ]), + 'multipart-body': coreapi.Link('/example/', action='post', encoding='multipart/form-data', fields=[ + coreapi.Field(name='example', location='body') + ]), + 'urlencoded': coreapi.Link('/example/', action='post', encoding='application/x-www-form-urlencoded', fields=[ + coreapi.Field(name='example') + ]), + 'urlencoded-body': coreapi.Link('/example/', action='post', encoding='application/x-www-form-urlencoded', fields=[ + coreapi.Field(name='example', location='body') + ]), + 'raw_upload': coreapi.Link('/upload/', action='post', encoding='application/octet-stream', fields=[ + coreapi.Field(name='example', location='body') + ]), + }, + 'response': { + 'download': coreapi.Link('/download/'), + 'text': coreapi.Link('/text/') + } + } + ) + + +def _iterlists(querydict): + if hasattr(querydict, 'iterlists'): + return querydict.iterlists() + return querydict.lists() + + +def _get_query_params(request): + # Return query params in a plain dict, using a list value if more + # than one item is present for a given key. + return { + key: (value[0] if len(value) == 1 else value) + for key, value in + _iterlists(request.query_params) + } + + +def _get_data(request): + if not isinstance(request.data, dict): + return request.data + # Coerce multidict into regular dict, and remove files to + # make assertions simpler. + if hasattr(request.data, 'iterlists') or hasattr(request.data, 'lists'): + # Use a list value if a QueryDict contains multiple items for a key. + return { + key: value[0] if len(value) == 1 else value + for key, value in _iterlists(request.data) + if key not in request.FILES + } + return { + key: value + for key, value in request.data.items() + if key not in request.FILES + } + + +def _get_files(request): + if not request.FILES: + return {} + return { + key: {'name': value.name, 'content': value.read()} + for key, value in request.FILES.items() + } + + +class SchemaView(APIView): + renderer_classes = [CoreJSONRenderer] + + def get(self, request): + schema = get_schema() + return Response(schema) + + +class ListView(APIView): + def get(self, request): + return Response({ + 'method': request.method, + 'query_params': _get_query_params(request) + }) + + def post(self, request): + if request.content_type: + content_type = request.content_type.split(';')[0] + else: + content_type = None + + return Response({ + 'method': request.method, + 'query_params': _get_query_params(request), + 'data': _get_data(request), + 'files': _get_files(request), + 'content_type': content_type + }) + + +class DetailView(APIView): + def get(self, request, id): + return Response({ + 'id': id, + 'method': request.method, + 'query_params': _get_query_params(request) + }) + + +class UploadView(APIView): + parser_classes = [FileUploadParser] + + def post(self, request): + return Response({ + 'method': request.method, + 'files': _get_files(request), + 'content_type': request.content_type + }) + + +class DownloadView(APIView): + def get(self, request): + return HttpResponse('some file content', content_type='image/png') + + +class TextView(APIView): + def get(self, request): + return HttpResponse('123', content_type='text/plain') + + +class HeadersView(APIView): + def get(self, request): + headers = { + key[5:].replace('_', '-'): value + for key, value in request.META.items() + if key.startswith('HTTP_') + } + return Response({ + 'method': request.method, + 'headers': headers + }) + + +urlpatterns = [ + url(r'^$', SchemaView.as_view()), + url(r'^example/$', ListView.as_view()), + url(r'^example/(?P[0-9]+)/$', DetailView.as_view()), + url(r'^upload/$', UploadView.as_view()), + url(r'^download/$', DownloadView.as_view()), + url(r'^text/$', TextView.as_view()), + url(r'^headers/$', HeadersView.as_view()), +] + + +@unittest.skipUnless(coreapi, 'coreapi not installed') +@override_settings(ROOT_URLCONF='tests.test_api_client') +class APIClientTests(APITestCase): + def test_api_client(self): + client = CoreAPIClient() + schema = client.get('http://api.example.com/') + assert schema.title == 'Example API' + assert schema.url == 'https://api.example.com/' + assert schema['simple_link'].description == 'example link' + assert schema['location']['query'].fields[0].description == 'example field' + data = client.action(schema, ['simple_link']) + expected = { + 'method': 'GET', + 'query_params': {} + } + assert data == expected + + def test_query_params(self): + client = CoreAPIClient() + schema = client.get('http://api.example.com/') + data = client.action(schema, ['location', 'query'], params={'example': 123}) + expected = { + 'method': 'GET', + 'query_params': {'example': '123'} + } + assert data == expected + + def test_session_headers(self): + client = CoreAPIClient() + client.session.headers.update({'X-Custom-Header': 'foo'}) + schema = client.get('http://api.example.com/') + data = client.action(schema, ['headers']) + assert data['headers']['X-CUSTOM-HEADER'] == 'foo' + + def test_query_params_with_multiple_values(self): + client = CoreAPIClient() + schema = client.get('http://api.example.com/') + data = client.action(schema, ['location', 'query'], params={'example': [1, 2, 3]}) + expected = { + 'method': 'GET', + 'query_params': {'example': ['1', '2', '3']} + } + assert data == expected + + def test_form_params(self): + client = CoreAPIClient() + schema = client.get('http://api.example.com/') + data = client.action(schema, ['location', 'form'], params={'example': 123}) + expected = { + 'method': 'POST', + 'content_type': 'application/json', + 'query_params': {}, + 'data': {'example': 123}, + 'files': {} + } + assert data == expected + + def test_body_params(self): + client = CoreAPIClient() + schema = client.get('http://api.example.com/') + data = client.action(schema, ['location', 'body'], params={'example': 123}) + expected = { + 'method': 'POST', + 'content_type': 'application/json', + 'query_params': {}, + 'data': 123, + 'files': {} + } + assert data == expected + + def test_path_params(self): + client = CoreAPIClient() + schema = client.get('http://api.example.com/') + data = client.action(schema, ['location', 'path'], params={'id': 123}) + expected = { + 'method': 'GET', + 'query_params': {}, + 'id': '123' + } + assert data == expected + + def test_multipart_encoding(self): + client = CoreAPIClient() + schema = client.get('http://api.example.com/') + + temp = tempfile.NamedTemporaryFile() + temp.write(b'example file content') + temp.flush() + + with open(temp.name, 'rb') as upload: + name = os.path.basename(upload.name) + data = client.action(schema, ['encoding', 'multipart'], params={'example': upload}) + + expected = { + 'method': 'POST', + 'content_type': 'multipart/form-data', + 'query_params': {}, + 'data': {}, + 'files': {'example': {'name': name, 'content': 'example file content'}} + } + assert data == expected + + def test_multipart_encoding_no_file(self): + # When no file is included, multipart encoding should still be used. + client = CoreAPIClient() + schema = client.get('http://api.example.com/') + + data = client.action(schema, ['encoding', 'multipart'], params={'example': 123}) + + expected = { + 'method': 'POST', + 'content_type': 'multipart/form-data', + 'query_params': {}, + 'data': {'example': '123'}, + 'files': {} + } + assert data == expected + + def test_multipart_encoding_multiple_values(self): + client = CoreAPIClient() + schema = client.get('http://api.example.com/') + + data = client.action(schema, ['encoding', 'multipart'], params={'example': [1, 2, 3]}) + + expected = { + 'method': 'POST', + 'content_type': 'multipart/form-data', + 'query_params': {}, + 'data': {'example': ['1', '2', '3']}, + 'files': {} + } + assert data == expected + + def test_multipart_encoding_string_file_content(self): + # Test for `coreapi.utils.File` support. + from coreapi.utils import File + + client = CoreAPIClient() + schema = client.get('http://api.example.com/') + + example = File(name='example.txt', content='123') + data = client.action(schema, ['encoding', 'multipart'], params={'example': example}) + + expected = { + 'method': 'POST', + 'content_type': 'multipart/form-data', + 'query_params': {}, + 'data': {}, + 'files': {'example': {'name': 'example.txt', 'content': '123'}} + } + assert data == expected + + def test_multipart_encoding_in_body(self): + from coreapi.utils import File + + client = CoreAPIClient() + schema = client.get('http://api.example.com/') + + example = {'foo': File(name='example.txt', content='123'), 'bar': 'abc'} + data = client.action(schema, ['encoding', 'multipart-body'], params={'example': example}) + + expected = { + 'method': 'POST', + 'content_type': 'multipart/form-data', + 'query_params': {}, + 'data': {'bar': 'abc'}, + 'files': {'foo': {'name': 'example.txt', 'content': '123'}} + } + assert data == expected + + # URLencoded + + def test_urlencoded_encoding(self): + client = CoreAPIClient() + schema = client.get('http://api.example.com/') + data = client.action(schema, ['encoding', 'urlencoded'], params={'example': 123}) + expected = { + 'method': 'POST', + 'content_type': 'application/x-www-form-urlencoded', + 'query_params': {}, + 'data': {'example': '123'}, + 'files': {} + } + assert data == expected + + def test_urlencoded_encoding_multiple_values(self): + client = CoreAPIClient() + schema = client.get('http://api.example.com/') + data = client.action(schema, ['encoding', 'urlencoded'], params={'example': [1, 2, 3]}) + expected = { + 'method': 'POST', + 'content_type': 'application/x-www-form-urlencoded', + 'query_params': {}, + 'data': {'example': ['1', '2', '3']}, + 'files': {} + } + assert data == expected + + def test_urlencoded_encoding_in_body(self): + client = CoreAPIClient() + schema = client.get('http://api.example.com/') + data = client.action(schema, ['encoding', 'urlencoded-body'], params={'example': {'foo': 123, 'bar': True}}) + expected = { + 'method': 'POST', + 'content_type': 'application/x-www-form-urlencoded', + 'query_params': {}, + 'data': {'foo': '123', 'bar': 'true'}, + 'files': {} + } + assert data == expected + + # Raw uploads + + def test_raw_upload(self): + client = CoreAPIClient() + schema = client.get('http://api.example.com/') + + temp = tempfile.NamedTemporaryFile() + temp.write(b'example file content') + temp.flush() + + with open(temp.name, 'rb') as upload: + name = os.path.basename(upload.name) + data = client.action(schema, ['encoding', 'raw_upload'], params={'example': upload}) + + expected = { + 'method': 'POST', + 'files': {'file': {'name': name, 'content': 'example file content'}}, + 'content_type': 'application/octet-stream' + } + assert data == expected + + def test_raw_upload_string_file_content(self): + from coreapi.utils import File + + client = CoreAPIClient() + schema = client.get('http://api.example.com/') + + example = File('example.txt', '123') + data = client.action(schema, ['encoding', 'raw_upload'], params={'example': example}) + + expected = { + 'method': 'POST', + 'files': {'file': {'name': 'example.txt', 'content': '123'}}, + 'content_type': 'text/plain' + } + assert data == expected + + def test_raw_upload_explicit_content_type(self): + from coreapi.utils import File + + client = CoreAPIClient() + schema = client.get('http://api.example.com/') + + example = File('example.txt', '123', 'text/html') + data = client.action(schema, ['encoding', 'raw_upload'], params={'example': example}) + + expected = { + 'method': 'POST', + 'files': {'file': {'name': 'example.txt', 'content': '123'}}, + 'content_type': 'text/html' + } + assert data == expected + + # Responses + + def test_text_response(self): + client = CoreAPIClient() + schema = client.get('http://api.example.com/') + + data = client.action(schema, ['response', 'text']) + + expected = '123' + assert data == expected + + def test_download_response(self): + client = CoreAPIClient() + schema = client.get('http://api.example.com/') + + data = client.action(schema, ['response', 'download']) + assert data.basename == 'download.png' + assert data.read() == b'some file content' diff --git a/tests/test_atomic_requests.py b/tests/test_atomic_requests.py index 8342ad3af..09d7f2fb1 100644 --- a/tests/test_atomic_requests.py +++ b/tests/test_atomic_requests.py @@ -5,7 +5,7 @@ import unittest from django.conf.urls import url from django.db import connection, connections, transaction from django.http import Http404 -from django.test import TestCase, TransactionTestCase +from django.test import TestCase, TransactionTestCase, override_settings from django.utils.decorators import method_decorator from rest_framework import status @@ -36,6 +36,20 @@ class APIExceptionView(APIView): raise APIException +class NonAtomicAPIExceptionView(APIView): + @method_decorator(transaction.non_atomic_requests) + def dispatch(self, *args, **kwargs): + return super(NonAtomicAPIExceptionView, self).dispatch(*args, **kwargs) + + def get(self, request, *args, **kwargs): + BasicModel.objects.all() + raise Http404 + +urlpatterns = ( + url(r'^$', NonAtomicAPIExceptionView.as_view()), +) + + @unittest.skipUnless( connection.features.uses_savepoints, "'atomic' requires transactions and savepoints." @@ -124,22 +138,8 @@ class DBTransactionAPIExceptionTests(TestCase): connection.features.uses_savepoints, "'atomic' requires transactions and savepoints." ) +@override_settings(ROOT_URLCONF='tests.test_atomic_requests') class NonAtomicDBTransactionAPIExceptionTests(TransactionTestCase): - @property - def urls(self): - class NonAtomicAPIExceptionView(APIView): - @method_decorator(transaction.non_atomic_requests) - def dispatch(self, *args, **kwargs): - return super(NonAtomicAPIExceptionView, self).dispatch(*args, **kwargs) - - def get(self, request, *args, **kwargs): - BasicModel.objects.all() - raise Http404 - - return ( - url(r'^$', NonAtomicAPIExceptionView.as_view()), - ) - def setUp(self): connections.databases['default']['ATOMIC_REQUESTS'] = True diff --git a/tests/test_authentication.py b/tests/test_authentication.py index 5ef620abe..6f17ea14f 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -20,6 +20,7 @@ from rest_framework.authentication import ( ) from rest_framework.authtoken.models import Token from rest_framework.authtoken.views import obtain_auth_token +from rest_framework.compat import is_authenticated from rest_framework.response import Response from rest_framework.test import APIClient, APIRequestFactory from rest_framework.views import APIView @@ -408,7 +409,7 @@ class FailingAuthAccessedInRenderer(TestCase): def render(self, data, media_type=None, renderer_context=None): request = renderer_context['request'] - if request.user.is_authenticated(): + if is_authenticated(request.user): return b'authenticated' return b'not authenticated' diff --git a/tests/test_fields.py b/tests/test_fields.py index 4a4b741c5..c271afa9e 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1,6 +1,7 @@ import datetime import os import re +import unittest import uuid from decimal import Decimal @@ -11,6 +12,67 @@ from django.utils import six, timezone import rest_framework from rest_framework import serializers +from rest_framework.fields import is_simple_callable + +try: + import typings +except ImportError: + typings = False + + +# Tests for helper functions. +# --------------------------- + +class TestIsSimpleCallable: + + def test_method(self): + class Foo: + @classmethod + def classmethod(cls): + pass + + def valid(self): + pass + + def valid_kwargs(self, param='value'): + pass + + def invalid(self, param): + pass + + assert is_simple_callable(Foo.classmethod) + + # unbound methods + assert not is_simple_callable(Foo.valid) + assert not is_simple_callable(Foo.valid_kwargs) + assert not is_simple_callable(Foo.invalid) + + # bound methods + assert is_simple_callable(Foo().valid) + assert is_simple_callable(Foo().valid_kwargs) + assert not is_simple_callable(Foo().invalid) + + def test_function(self): + def simple(): + pass + + def valid(param='value', param2='value'): + pass + + def invalid(param, param2='value'): + pass + + assert is_simple_callable(simple) + assert is_simple_callable(valid) + assert not is_simple_callable(invalid) + + @unittest.skipUnless(typings, 'requires python 3.5') + def test_type_annotation(self): + # The annotation will otherwise raise a syntax error in python < 3.5 + exec("def valid(param: str='value'): pass", locals()) + valid = locals()['valid'] + + assert is_simple_callable(valid) # Tests for field keyword arguments and core functionality. diff --git a/tests/test_filters.py b/tests/test_filters.py index 03d61fc37..c67412dd7 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -6,7 +6,6 @@ from decimal import Decimal from django.conf.urls import url from django.core.exceptions import ImproperlyConfigured -from django.core.urlresolvers import reverse from django.db import models from django.test import TestCase from django.test.utils import override_settings @@ -14,7 +13,7 @@ from django.utils.dateparse import parse_date from django.utils.six.moves import reload_module from rest_framework import filters, generics, serializers, status -from rest_framework.compat import django_filters +from rest_framework.compat import django_filters, reverse from rest_framework.test import APIRequestFactory from .models import BaseFilterableItem, BasicModel, FilterableItem @@ -77,6 +76,7 @@ if django_filters: class Meta: model = BaseFilterableItem + fields = '__all__' class BaseFilterableItemFilterRootView(generics.ListCreateAPIView): queryset = FilterableItem.objects.all() @@ -456,7 +456,7 @@ class AttributeModel(models.Model): class SearchFilterModelFk(models.Model): title = models.CharField(max_length=20) - attribute = models.ForeignKey(AttributeModel) + attribute = models.ForeignKey(AttributeModel, on_delete=models.CASCADE) class SearchFilterFkSerializer(serializers.ModelSerializer): diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 01243ff6e..cd9b2dfc3 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -20,7 +20,7 @@ from django.test import TestCase from django.utils import six from rest_framework import serializers -from rest_framework.compat import unicode_repr +from rest_framework.compat import set_many, unicode_repr def dedent(blocktext): @@ -651,7 +651,7 @@ class TestIntegration(TestCase): foreign_key=self.foreign_key_target, one_to_one=self.one_to_one_target, ) - self.instance.many_to_many = self.many_to_many_targets + set_many(self.instance, 'many_to_many', self.many_to_many_targets) self.instance.save() def test_pk_retrival(self): @@ -962,7 +962,7 @@ class OneToOneTargetTestModel(models.Model): class OneToOneSourceTestModel(models.Model): - target = models.OneToOneField(OneToOneTargetTestModel, primary_key=True) + target = models.OneToOneField(OneToOneTargetTestModel, primary_key=True, on_delete=models.CASCADE) class TestModelFieldValues(TestCase): @@ -990,6 +990,7 @@ class TestUniquenessOverride(TestCase): class TestSerializer(serializers.ModelSerializer): class Meta: model = TestModel + fields = '__all__' extra_kwargs = {'field_1': {'required': False}} fields = TestSerializer().fields diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 5cef22628..f8561e61d 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -4,7 +4,6 @@ import base64 import unittest from django.contrib.auth.models import Group, Permission, User -from django.core.urlresolvers import ResolverMatch from django.db import models from django.test import TestCase @@ -12,7 +11,7 @@ from rest_framework import ( HTTP_HEADER_ENCODING, authentication, generics, permissions, serializers, status ) -from rest_framework.compat import guardian +from rest_framework.compat import ResolverMatch, guardian, set_many from rest_framework.filters import DjangoObjectPermissionsFilter from rest_framework.routers import DefaultRouter from rest_framework.test import APIRequestFactory @@ -74,15 +73,15 @@ class ModelPermissionsIntegrationTests(TestCase): def setUp(self): User.objects.create_user('disallowed', 'disallowed@example.com', 'password') user = User.objects.create_user('permitted', 'permitted@example.com', 'password') - user.user_permissions = [ + set_many(user, 'user_permissions', [ Permission.objects.get(codename='add_basicmodel'), Permission.objects.get(codename='change_basicmodel'), Permission.objects.get(codename='delete_basicmodel') - ] + ]) user = User.objects.create_user('updateonly', 'updateonly@example.com', 'password') - user.user_permissions = [ + set_many(user, 'user_permissions', [ Permission.objects.get(codename='change_basicmodel'), - ] + ]) self.permitted_credentials = basic_auth_header('permitted', 'password') self.disallowed_credentials = basic_auth_header('disallowed', 'password') diff --git a/tests/test_request.py b/tests/test_request.py index dbfa695fd..32fbbc50b 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -13,6 +13,7 @@ from django.utils import six from rest_framework import status from rest_framework.authentication import SessionAuthentication +from rest_framework.compat import is_anonymous from rest_framework.parsers import BaseParser, FormParser, MultiPartParser from rest_framework.request import Request from rest_framework.response import Response @@ -169,9 +170,9 @@ class TestUserSetter(TestCase): def test_user_can_logout(self): self.request.user = self.user - self.assertFalse(self.request.user.is_anonymous()) + self.assertFalse(is_anonymous(self.request.user)) logout(self.request) - self.assertTrue(self.request.user.is_anonymous()) + self.assertTrue(is_anonymous(self.request.user)) def test_logged_in_user_is_set_on_wrapped_request(self): login(self.request, self.user) diff --git a/tests/test_requests_client.py b/tests/test_requests_client.py new file mode 100644 index 000000000..791ca4ff2 --- /dev/null +++ b/tests/test_requests_client.py @@ -0,0 +1,256 @@ +from __future__ import unicode_literals + +import unittest + +from django.conf.urls import url +from django.contrib.auth import authenticate, login +from django.contrib.auth.models import User +from django.shortcuts import redirect +from django.test import override_settings +from django.utils.decorators import method_decorator +from django.views.decorators.csrf import csrf_protect, ensure_csrf_cookie + +from rest_framework.compat import is_authenticated, requests +from rest_framework.response import Response +from rest_framework.test import APITestCase, RequestsClient +from rest_framework.views import APIView + + +class Root(APIView): + def get(self, request): + return Response({ + 'method': request.method, + 'query_params': request.query_params, + }) + + def post(self, request): + files = { + key: (value.name, value.read()) + for key, value in request.FILES.items() + } + post = request.POST + json = None + if request.META.get('CONTENT_TYPE') == 'application/json': + json = request.data + + return Response({ + 'method': request.method, + 'query_params': request.query_params, + 'POST': post, + 'FILES': files, + 'JSON': json + }) + + +class HeadersView(APIView): + def get(self, request): + headers = { + key[5:].replace('_', '-'): value + for key, value in request.META.items() + if key.startswith('HTTP_') + } + return Response({ + 'method': request.method, + 'headers': headers + }) + + +class SessionView(APIView): + def get(self, request): + return Response({ + key: value for key, value in request.session.items() + }) + + def post(self, request): + for key, value in request.data.items(): + request.session[key] = value + return Response({ + key: value for key, value in request.session.items() + }) + + +class AuthView(APIView): + @method_decorator(ensure_csrf_cookie) + def get(self, request): + if is_authenticated(request.user): + username = request.user.username + else: + username = None + return Response({ + 'username': username + }) + + @method_decorator(csrf_protect) + def post(self, request): + username = request.data['username'] + password = request.data['password'] + user = authenticate(username=username, password=password) + if user is None: + return Response({'error': 'incorrect credentials'}) + login(request, user) + return redirect('/auth/') + + +urlpatterns = [ + url(r'^$', Root.as_view(), name='root'), + url(r'^headers/$', HeadersView.as_view(), name='headers'), + url(r'^session/$', SessionView.as_view(), name='session'), + url(r'^auth/$', AuthView.as_view(), name='auth'), +] + + +@unittest.skipUnless(requests, 'requests not installed') +@override_settings(ROOT_URLCONF='tests.test_requests_client') +class RequestsClientTests(APITestCase): + def test_get_request(self): + client = RequestsClient() + response = client.get('http://testserver/') + assert response.status_code == 200 + assert response.headers['Content-Type'] == 'application/json' + expected = { + 'method': 'GET', + 'query_params': {} + } + assert response.json() == expected + + def test_get_request_query_params_in_url(self): + client = RequestsClient() + response = client.get('http://testserver/?key=value') + assert response.status_code == 200 + assert response.headers['Content-Type'] == 'application/json' + expected = { + 'method': 'GET', + 'query_params': {'key': 'value'} + } + assert response.json() == expected + + def test_get_request_query_params_by_kwarg(self): + client = RequestsClient() + response = client.get('http://testserver/', params={'key': 'value'}) + assert response.status_code == 200 + assert response.headers['Content-Type'] == 'application/json' + expected = { + 'method': 'GET', + 'query_params': {'key': 'value'} + } + assert response.json() == expected + + def test_get_with_headers(self): + client = RequestsClient() + response = client.get('http://testserver/headers/', headers={'User-Agent': 'example'}) + assert response.status_code == 200 + assert response.headers['Content-Type'] == 'application/json' + headers = response.json()['headers'] + assert headers['USER-AGENT'] == 'example' + + def test_get_with_session_headers(self): + client = RequestsClient() + client.headers.update({'User-Agent': 'example'}) + response = client.get('http://testserver/headers/') + assert response.status_code == 200 + assert response.headers['Content-Type'] == 'application/json' + headers = response.json()['headers'] + assert headers['USER-AGENT'] == 'example' + + def test_post_form_request(self): + client = RequestsClient() + response = client.post('http://testserver/', data={'key': 'value'}) + assert response.status_code == 200 + assert response.headers['Content-Type'] == 'application/json' + expected = { + 'method': 'POST', + 'query_params': {}, + 'POST': {'key': 'value'}, + 'FILES': {}, + 'JSON': None + } + assert response.json() == expected + + def test_post_json_request(self): + client = RequestsClient() + response = client.post('http://testserver/', json={'key': 'value'}) + assert response.status_code == 200 + assert response.headers['Content-Type'] == 'application/json' + expected = { + 'method': 'POST', + 'query_params': {}, + 'POST': {}, + 'FILES': {}, + 'JSON': {'key': 'value'} + } + assert response.json() == expected + + def test_post_multipart_request(self): + client = RequestsClient() + files = { + 'file': ('report.csv', 'some,data,to,send\nanother,row,to,send\n') + } + response = client.post('http://testserver/', files=files) + assert response.status_code == 200 + assert response.headers['Content-Type'] == 'application/json' + expected = { + 'method': 'POST', + 'query_params': {}, + 'FILES': {'file': ['report.csv', 'some,data,to,send\nanother,row,to,send\n']}, + 'POST': {}, + 'JSON': None + } + assert response.json() == expected + + def test_session(self): + client = RequestsClient() + response = client.get('http://testserver/session/') + assert response.status_code == 200 + assert response.headers['Content-Type'] == 'application/json' + expected = {} + assert response.json() == expected + + response = client.post('http://testserver/session/', json={'example': 'abc'}) + assert response.status_code == 200 + assert response.headers['Content-Type'] == 'application/json' + expected = {'example': 'abc'} + assert response.json() == expected + + response = client.get('http://testserver/session/') + assert response.status_code == 200 + assert response.headers['Content-Type'] == 'application/json' + expected = {'example': 'abc'} + assert response.json() == expected + + def test_auth(self): + # Confirm session is not authenticated + client = RequestsClient() + response = client.get('http://testserver/auth/') + assert response.status_code == 200 + assert response.headers['Content-Type'] == 'application/json' + expected = { + 'username': None + } + assert response.json() == expected + assert 'csrftoken' in response.cookies + csrftoken = response.cookies['csrftoken'] + + user = User.objects.create(username='tom') + user.set_password('password') + user.save() + + # Perform a login + response = client.post('http://testserver/auth/', json={ + 'username': 'tom', + 'password': 'password' + }, headers={'X-CSRFToken': csrftoken}) + assert response.status_code == 200 + assert response.headers['Content-Type'] == 'application/json' + expected = { + 'username': 'tom' + } + assert response.json() == expected + + # Confirm session is authenticated + response = client.get('http://testserver/auth/') + assert response.status_code == 200 + assert response.headers['Content-Type'] == 'application/json' + expected = { + 'username': 'tom' + } + assert response.json() == expected diff --git a/tests/test_reverse.py b/tests/test_reverse.py index 03d31f1f9..f30a8bf9a 100644 --- a/tests/test_reverse.py +++ b/tests/test_reverse.py @@ -1,9 +1,9 @@ from __future__ import unicode_literals from django.conf.urls import url -from django.core.urlresolvers import NoReverseMatch from django.test import TestCase, override_settings +from rest_framework.compat import NoReverseMatch from rest_framework.reverse import reverse from rest_framework.test import APIRequestFactory diff --git a/tests/test_routers.py b/tests/test_routers.py index f45039f80..d28e301a0 100644 --- a/tests/test_routers.py +++ b/tests/test_routers.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals +import json from collections import namedtuple from django.conf.urls import include, url @@ -47,6 +48,21 @@ class MockViewSet(viewsets.ModelViewSet): serializer_class = None +class EmptyPrefixSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = RouterTestModel + fields = ('uuid', 'text') + + +class EmptyPrefixViewSet(viewsets.ModelViewSet): + queryset = [RouterTestModel(id=1, uuid='111', text='First'), RouterTestModel(id=2, uuid='222', text='Second')] + serializer_class = EmptyPrefixSerializer + + def get_object(self, *args, **kwargs): + index = int(self.kwargs['pk']) - 1 + return self.queryset[index] + + notes_router = SimpleRouter() notes_router.register(r'notes', NoteViewSet) @@ -56,11 +72,19 @@ kwarged_notes_router.register(r'notes', KWargedNoteViewSet) namespaced_router = DefaultRouter() namespaced_router.register(r'example', MockViewSet, base_name='example') +empty_prefix_router = SimpleRouter() +empty_prefix_router.register(r'', EmptyPrefixViewSet, base_name='empty_prefix') +empty_prefix_urls = [ + url(r'^', include(empty_prefix_router.urls)), +] + urlpatterns = [ url(r'^non-namespaced/', include(namespaced_router.urls)), url(r'^namespaced/', include(namespaced_router.urls, namespace='example')), url(r'^example/', include(notes_router.urls)), url(r'^example2/', include(kwarged_notes_router.urls)), + + url(r'^empty-prefix/', include(empty_prefix_urls)), ] @@ -384,3 +408,28 @@ class TestDynamicListAndDetailRouter(TestCase): def test_inherited_list_and_detail_route_decorators(self): self._test_list_and_detail_route_decorators(SubDynamicListAndDetailViewSet) + + +@override_settings(ROOT_URLCONF='tests.test_routers') +class TestEmptyPrefix(TestCase): + def test_empty_prefix_list(self): + response = self.client.get('/empty-prefix/') + self.assertEqual(200, response.status_code) + self.assertEqual( + json.loads(response.content.decode('utf-8')), + [ + {'uuid': '111', 'text': 'First'}, + {'uuid': '222', 'text': 'Second'} + ] + ) + + def test_empty_prefix_detail(self): + response = self.client.get('/empty-prefix/1/') + self.assertEqual(200, response.status_code) + self.assertEqual( + json.loads(response.content.decode('utf-8')), + { + 'uuid': '111', + 'text': 'First' + } + ) diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 197e62eb0..c43fc1eff 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -6,9 +6,8 @@ from django.test import TestCase, override_settings from rest_framework import filters, pagination, permissions, serializers from rest_framework.compat import coreapi from rest_framework.decorators import detail_route, list_route -from rest_framework.response import Response from rest_framework.routers import DefaultRouter -from rest_framework.schemas import SchemaGenerator +from rest_framework.schemas import SchemaGenerator, get_schema_view from rest_framework.test import APIClient from rest_framework.views import APIView from rest_framework.viewsets import ModelViewSet @@ -23,6 +22,10 @@ class ExamplePagination(pagination.PageNumberPagination): page_size = 100 +class EmptySerializer(serializers.Serializer): + pass + + class ExampleSerializer(serializers.Serializer): a = serializers.CharField(required=True, help_text='A field description') b = serializers.CharField(required=False) @@ -43,36 +46,37 @@ class ExampleViewSet(ModelViewSet): @detail_route(methods=['post'], serializer_class=AnotherSerializer) def custom_action(self, request, pk): + """ + A description of custom action. + """ return super(ExampleSerializer, self).retrieve(self, request) @list_route() def custom_list_action(self, request): return super(ExampleViewSet, self).list(self, request) + @list_route(methods=['post', 'get'], serializer_class=EmptySerializer) + def custom_list_action_multiple_methods(self, request): + return super(ExampleViewSet, self).list(self, request) + def get_serializer(self, *args, **kwargs): assert self.request assert self.action return super(ExampleViewSet, self).get_serializer(*args, **kwargs) -class ExampleView(APIView): - permission_classes = [permissions.IsAuthenticatedOrReadOnly] +if coreapi: + schema_view = get_schema_view(title='Example API') +else: + def schema_view(request): + pass - def get(self, request, *args, **kwargs): - return Response() - - def post(self, request, *args, **kwargs): - return Response() - - -router = DefaultRouter(schema_title='Example API' if coreapi else None) +router = DefaultRouter() router.register('example', ExampleViewSet, base_name='example') urlpatterns = [ + url(r'^$', schema_view), url(r'^', include(router.urls)) ] -urlpatterns2 = [ - url(r'^example-view/$', ExampleView.as_view(), name='example-view') -] @unittest.skipUnless(coreapi, 'coreapi is not installed') @@ -80,7 +84,7 @@ urlpatterns2 = [ class TestRouterGeneratedSchema(TestCase): def test_anonymous_request(self): client = APIClient() - response = client.get('/', HTTP_ACCEPT='application/vnd.coreapi+json') + response = client.get('/', HTTP_ACCEPT='application/coreapi+json') self.assertEqual(response.status_code, 200) expected = coreapi.Document( url='', @@ -99,11 +103,17 @@ class TestRouterGeneratedSchema(TestCase): url='/example/custom_list_action/', action='get' ), - 'retrieve': coreapi.Link( - url='/example/{pk}/', + 'custom_list_action_multiple_methods': { + 'read': coreapi.Link( + url='/example/custom_list_action_multiple_methods/', + action='get' + ) + }, + 'read': coreapi.Link( + url='/example/{id}/', action='get', fields=[ - coreapi.Field('pk', required=True, location='path') + coreapi.Field('id', required=True, location='path') ] ) } @@ -114,7 +124,7 @@ class TestRouterGeneratedSchema(TestCase): def test_authenticated_request(self): client = APIClient() client.force_authenticate(MockUser()) - response = client.get('/', HTTP_ACCEPT='application/vnd.coreapi+json') + response = client.get('/', HTTP_ACCEPT='application/coreapi+json') self.assertEqual(response.status_code, 200) expected = coreapi.Document( url='', @@ -134,56 +144,67 @@ class TestRouterGeneratedSchema(TestCase): action='post', encoding='application/json', fields=[ - coreapi.Field('a', required=True, location='form', description='A field description'), - coreapi.Field('b', required=False, location='form') + coreapi.Field('a', required=True, location='form', type='string', description='A field description'), + coreapi.Field('b', required=False, location='form', type='string') ] ), - 'retrieve': coreapi.Link( - url='/example/{pk}/', + 'read': coreapi.Link( + url='/example/{id}/', action='get', fields=[ - coreapi.Field('pk', required=True, location='path') + coreapi.Field('id', required=True, location='path') ] ), 'custom_action': coreapi.Link( - url='/example/{pk}/custom_action/', + url='/example/{id}/custom_action/', action='post', encoding='application/json', + description='A description of custom action.', fields=[ - coreapi.Field('pk', required=True, location='path'), - coreapi.Field('c', required=True, location='form'), - coreapi.Field('d', required=False, location='form'), + coreapi.Field('id', required=True, location='path'), + coreapi.Field('c', required=True, location='form', type='string'), + coreapi.Field('d', required=False, location='form', type='string'), ] ), 'custom_list_action': coreapi.Link( url='/example/custom_list_action/', action='get' ), + 'custom_list_action_multiple_methods': { + 'read': coreapi.Link( + url='/example/custom_list_action_multiple_methods/', + action='get' + ), + 'create': coreapi.Link( + url='/example/custom_list_action_multiple_methods/', + action='post' + ) + }, 'update': coreapi.Link( - url='/example/{pk}/', + url='/example/{id}/', action='put', encoding='application/json', fields=[ - coreapi.Field('pk', required=True, location='path'), - coreapi.Field('a', required=True, location='form', description='A field description'), - coreapi.Field('b', required=False, location='form') + coreapi.Field('id', required=True, location='path'), + coreapi.Field('a', required=True, location='form', type='string', description='A field description'), + coreapi.Field('b', required=False, location='form', type='string') ] ), 'partial_update': coreapi.Link( - url='/example/{pk}/', + url='/example/{id}/', action='patch', encoding='application/json', fields=[ - coreapi.Field('pk', required=True, location='path'), - coreapi.Field('a', required=False, location='form', description='A field description'), - coreapi.Field('b', required=False, location='form') + coreapi.Field('id', required=True, location='path'), + coreapi.Field('a', required=False, location='form', type='string', description='A field description'), + coreapi.Field('b', required=False, location='form', type='string') ] ), - 'destroy': coreapi.Link( - url='/example/{pk}/', + 'delete': coreapi.Link( + url='/example/{id}/', action='delete', fields=[ - coreapi.Field('pk', required=True, location='path') + coreapi.Field('id', required=True, location='path') ] ) } @@ -192,27 +213,123 @@ class TestRouterGeneratedSchema(TestCase): self.assertEqual(response.data, expected) +class ExampleListView(APIView): + permission_classes = [permissions.IsAuthenticatedOrReadOnly] + + def get(self, *args, **kwargs): + pass + + def post(self, request, *args, **kwargs): + pass + + +class ExampleDetailView(APIView): + permission_classes = [permissions.IsAuthenticatedOrReadOnly] + + def get(self, *args, **kwargs): + pass + + @unittest.skipUnless(coreapi, 'coreapi is not installed') class TestSchemaGenerator(TestCase): - def test_view(self): - schema_generator = SchemaGenerator(title='Test View', patterns=urlpatterns2) - schema = schema_generator.get_schema() + def setUp(self): + self.patterns = [ + url('^example/?$', ExampleListView.as_view()), + url('^example/(?P\d+)/?$', ExampleDetailView.as_view()), + url('^example/(?P\d+)/sub/?$', ExampleDetailView.as_view()), + ] + + def test_schema_for_regular_views(self): + """ + Ensure that schema generation works for APIView classes. + """ + generator = SchemaGenerator(title='Example API', patterns=self.patterns) + schema = generator.get_schema() expected = coreapi.Document( url='', - title='Test View', + title='Example API', content={ - 'example-view': { + 'example': { 'create': coreapi.Link( - url='/example-view/', + url='/example/', action='post', fields=[] ), - 'read': coreapi.Link( - url='/example-view/', + 'list': coreapi.Link( + url='/example/', action='get', fields=[] - ) + ), + 'read': coreapi.Link( + url='/example/{id}/', + action='get', + fields=[ + coreapi.Field('id', required=True, location='path') + ] + ), + 'sub': { + 'list': coreapi.Link( + url='/example/{id}/sub/', + action='get', + fields=[ + coreapi.Field('id', required=True, location='path') + ] + ) + } } } ) - self.assertEquals(schema, expected) + self.assertEqual(schema, expected) + + +@unittest.skipUnless(coreapi, 'coreapi is not installed') +class TestSchemaGeneratorNotAtRoot(TestCase): + def setUp(self): + self.patterns = [ + url('^api/v1/example/?$', ExampleListView.as_view()), + url('^api/v1/example/(?P\d+)/?$', ExampleDetailView.as_view()), + url('^api/v1/example/(?P\d+)/sub/?$', ExampleDetailView.as_view()), + ] + + def test_schema_for_regular_views(self): + """ + Ensure that schema generation with an API that is not at the URL + root continues to use correct structure for link keys. + """ + generator = SchemaGenerator(title='Example API', patterns=self.patterns) + schema = generator.get_schema() + expected = coreapi.Document( + url='', + title='Example API', + content={ + 'example': { + 'create': coreapi.Link( + url='/api/v1/example/', + action='post', + fields=[] + ), + 'list': coreapi.Link( + url='/api/v1/example/', + action='get', + fields=[] + ), + 'read': coreapi.Link( + url='/api/v1/example/{id}/', + action='get', + fields=[ + coreapi.Field('id', required=True, location='path') + ] + ), + 'sub': { + 'list': coreapi.Link( + url='/api/v1/example/{id}/sub/', + action='get', + fields=[ + coreapi.Field('id', required=True, location='path') + ] + ) + } + } + } + ) + self.assertEqual(schema, expected) diff --git a/tests/test_urlpatterns.py b/tests/test_urlpatterns.py index 78d37c1a8..33d367e1d 100644 --- a/tests/test_urlpatterns.py +++ b/tests/test_urlpatterns.py @@ -3,9 +3,9 @@ from __future__ import unicode_literals from collections import namedtuple from django.conf.urls import include, url -from django.core import urlresolvers from django.test import TestCase +from rest_framework.compat import RegexURLResolver, Resolver404 from rest_framework.test import APIRequestFactory from rest_framework.urlpatterns import format_suffix_patterns @@ -28,7 +28,7 @@ class FormatSuffixTests(TestCase): urlpatterns = format_suffix_patterns(urlpatterns) except Exception: self.fail("Failed to apply `format_suffix_patterns` on the supplied urlpatterns") - resolver = urlresolvers.RegexURLResolver(r'^/', urlpatterns) + resolver = RegexURLResolver(r'^/', urlpatterns) for test_path in test_paths: request = factory.get(test_path.path) try: @@ -43,7 +43,7 @@ class FormatSuffixTests(TestCase): urlpatterns = format_suffix_patterns([ url(r'^test/$', dummy_view), ]) - resolver = urlresolvers.RegexURLResolver(r'^/', urlpatterns) + resolver = RegexURLResolver(r'^/', urlpatterns) test_paths = [ (URLTestPath('/test.api', (), {'format': 'api'}), True), @@ -55,7 +55,7 @@ class FormatSuffixTests(TestCase): request = factory.get(test_path.path) try: callback, callback_args, callback_kwargs = resolver.resolve(request.path_info) - except urlresolvers.Resolver404: + except Resolver404: callback, callback_args, callback_kwargs = (None, None, None) if not expected_resolved: assert callback is None diff --git a/tests/utils.py b/tests/utils.py index 5b2d75864..52582f093 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,5 +1,5 @@ from django.core.exceptions import ObjectDoesNotExist -from django.core.urlresolvers import NoReverseMatch +from rest_framework.compat import NoReverseMatch class MockObject(object): diff --git a/tox.ini b/tox.ini index 48cecccf7..bac1569a0 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ addopts=--tb=short [tox] envlist = py27-{lint,docs}, - {py27,py32,py33,py34,py35}-django18, + {py27,py33,py34,py35}-django18, {py27,py34,py35}-django19, {py27,py34,py35}-django110, {py27,py34,py35}-django{master} @@ -25,7 +25,6 @@ basepython = py35: python3.5 py34: python3.4 py33: python3.3 - py32: python3.2 py27: python2.7 [testenv:py27-lint] From 072d14c2e1d4cf5d0b57196296657f19dd807e8c Mon Sep 17 00:00:00 2001 From: Steven Johns Date: Tue, 11 Oct 2016 11:20:48 +1100 Subject: [PATCH 129/175] Corrected `artist` and `album_name` `The Roots` are the band: https://en.wikipedia.org/wiki/The_Roots `Undun` is their album: https://en.wikipedia.org/wiki/Undun --- docs/api-guide/relations.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md index 8fde53d3a..aeefae8b1 100644 --- a/docs/api-guide/relations.md +++ b/docs/api-guide/relations.md @@ -99,8 +99,8 @@ For example, the following serializer: Would serialize to a representation like this: { - 'album_name': 'The Roots', - 'artist': 'Undun', + 'album_name': 'Undun', + 'artist': 'The Roots', 'tracks': [ 89, 90, From a3802504a0a26f1d03d2cf6f851030314b7372fd Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 11 Oct 2016 10:25:21 +0100 Subject: [PATCH 130/175] Error codes (#4550) Add error codes to `APIException` --- docs/api-guide/exceptions.md | 54 ++++++++-- rest_framework/authtoken/serializers.py | 6 +- rest_framework/exceptions.py | 132 +++++++++++++++++------- rest_framework/fields.py | 18 +++- rest_framework/serializers.py | 35 +++---- rest_framework/validators.py | 21 ++-- tests/test_exceptions.py | 38 +++++-- tests/test_validation.py | 2 +- tests/test_validation_error.py | 101 ++++++++++++++++++ 9 files changed, 316 insertions(+), 91 deletions(-) create mode 100644 tests/test_validation_error.py diff --git a/docs/api-guide/exceptions.md b/docs/api-guide/exceptions.md index 3e4b3e8be..f0f178d92 100644 --- a/docs/api-guide/exceptions.md +++ b/docs/api-guide/exceptions.md @@ -98,7 +98,7 @@ Note that the exception handler will only be called for responses generated by r The **base class** for all exceptions raised inside an `APIView` class or `@api_view`. -To provide a custom exception, subclass `APIException` and set the `.status_code` and `.default_detail` properties on the class. +To provide a custom exception, subclass `APIException` and set the `.status_code`, `.default_detail`, and `default_code` attributes on the class. For example, if your API relies on a third party service that may sometimes be unreachable, you might want to implement an exception for the "503 Service Unavailable" HTTP response code. You could do this like so: @@ -107,10 +107,42 @@ For example, if your API relies on a third party service that may sometimes be u class ServiceUnavailable(APIException): status_code = 503 default_detail = 'Service temporarily unavailable, try again later.' + default_code = 'service_unavailable' + +#### Inspecting API exceptions + +There are a number of different properties available for inspecting the status +of an API exception. You can use these to build custom exception handling +for your project. + +The available attributes and methods are: + +* `.detail` - Return the textual description of the error. +* `.get_codes()` - Return the code identifier of the error. +* `.full_details()` - Return both the textual description and the code identifier. + +In most cases the error detail will be a simple item: + + >>> print(exc.detail) + You do not have permission to perform this action. + >>> print(exc.get_codes()) + permission_denied + >>> print(exc.full_details()) + {'message':'You do not have permission to perform this action.','code':'permission_denied'} + +In the case of validation errors the error detail will be either a list or +dictionary of items: + + >>> print(exc.detail) + {"name":"This field is required.","age":"A valid integer is required."} + >>> print(exc.get_codes()) + {"name":"required","age":"invalid"} + >>> print(exc.get_full_details()) + {"name":{"message":"This field is required.","code":"required"},"age":{"message":"A valid integer is required.","code":"invalid"}} ## ParseError -**Signature:** `ParseError(detail=None)` +**Signature:** `ParseError(detail=None, code=None)` Raised if the request contains malformed data when accessing `request.data`. @@ -118,7 +150,7 @@ By default this exception results in a response with the HTTP status code "400 B ## AuthenticationFailed -**Signature:** `AuthenticationFailed(detail=None)` +**Signature:** `AuthenticationFailed(detail=None, code=None)` Raised when an incoming request includes incorrect authentication. @@ -126,7 +158,7 @@ By default this exception results in a response with the HTTP status code "401 U ## NotAuthenticated -**Signature:** `NotAuthenticated(detail=None)` +**Signature:** `NotAuthenticated(detail=None, code=None)` Raised when an unauthenticated request fails the permission checks. @@ -134,7 +166,7 @@ By default this exception results in a response with the HTTP status code "401 U ## PermissionDenied -**Signature:** `PermissionDenied(detail=None)` +**Signature:** `PermissionDenied(detail=None, code=None)` Raised when an authenticated request fails the permission checks. @@ -142,7 +174,7 @@ By default this exception results in a response with the HTTP status code "403 F ## NotFound -**Signature:** `NotFound(detail=None)` +**Signature:** `NotFound(detail=None, code=None)` Raised when a resource does not exists at the given URL. This exception is equivalent to the standard `Http404` Django exception. @@ -150,7 +182,7 @@ By default this exception results in a response with the HTTP status code "404 N ## MethodNotAllowed -**Signature:** `MethodNotAllowed(method, detail=None)` +**Signature:** `MethodNotAllowed(method, detail=None, code=None)` Raised when an incoming request occurs that does not map to a handler method on the view. @@ -158,7 +190,7 @@ By default this exception results in a response with the HTTP status code "405 M ## NotAcceptable -**Signature:** `NotAcceptable(detail=None)` +**Signature:** `NotAcceptable(detail=None, code=None)` Raised when an incoming request occurs with an `Accept` header that cannot be satisfied by any of the available renderers. @@ -166,7 +198,7 @@ By default this exception results in a response with the HTTP status code "406 N ## UnsupportedMediaType -**Signature:** `UnsupportedMediaType(media_type, detail=None)` +**Signature:** `UnsupportedMediaType(media_type, detail=None, code=None)` Raised if there are no parsers that can handle the content type of the request data when accessing `request.data`. @@ -174,7 +206,7 @@ By default this exception results in a response with the HTTP status code "415 U ## Throttled -**Signature:** `Throttled(wait=None, detail=None)` +**Signature:** `Throttled(wait=None, detail=None, code=None)` Raised when an incoming request fails the throttling checks. @@ -182,7 +214,7 @@ By default this exception results in a response with the HTTP status code "429 T ## ValidationError -**Signature:** `ValidationError(detail)` +**Signature:** `ValidationError(detail, code=None)` The `ValidationError` exception is slightly different from the other `APIException` classes: diff --git a/rest_framework/authtoken/serializers.py b/rest_framework/authtoken/serializers.py index 90d3bd96e..b91a8454f 100644 --- a/rest_framework/authtoken/serializers.py +++ b/rest_framework/authtoken/serializers.py @@ -21,13 +21,13 @@ class AuthTokenSerializer(serializers.Serializer): # (Assuming the default `ModelBackend` authentication backend.) if not user.is_active: msg = _('User account is disabled.') - raise serializers.ValidationError(msg) + raise serializers.ValidationError(msg, code='authorization') else: msg = _('Unable to log in with provided credentials.') - raise serializers.ValidationError(msg) + raise serializers.ValidationError(msg, code='authorization') else: msg = _('Must include "username" and "password".') - raise serializers.ValidationError(msg) + raise serializers.ValidationError(msg, code='authorization') attrs['user'] = user return attrs diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py index 29afaffe0..e41655fef 100644 --- a/rest_framework/exceptions.py +++ b/rest_framework/exceptions.py @@ -17,27 +17,61 @@ from rest_framework import status from rest_framework.utils.serializer_helpers import ReturnDict, ReturnList -def _force_text_recursive(data): +def _get_error_details(data, default_code=None): """ Descend into a nested data structure, forcing any - lazy translation strings into plain text. + lazy translation strings or strings into `ErrorDetail`. """ if isinstance(data, list): ret = [ - _force_text_recursive(item) for item in data + _get_error_details(item, default_code) for item in data ] if isinstance(data, ReturnList): return ReturnList(ret, serializer=data.serializer) return ret elif isinstance(data, dict): ret = { - key: _force_text_recursive(value) + key: _get_error_details(value, default_code) for key, value in data.items() } if isinstance(data, ReturnDict): return ReturnDict(ret, serializer=data.serializer) return ret - return force_text(data) + + text = force_text(data) + code = getattr(data, 'code', default_code) + return ErrorDetail(text, code) + + +def _get_codes(detail): + if isinstance(detail, list): + return [_get_codes(item) for item in detail] + elif isinstance(detail, dict): + return {key: _get_codes(value) for key, value in detail.items()} + return detail.code + + +def _get_full_details(detail): + if isinstance(detail, list): + return [_get_full_details(item) for item in detail] + elif isinstance(detail, dict): + return {key: _get_full_details(value) for key, value in detail.items()} + return { + 'message': detail, + 'code': detail.code + } + + +class ErrorDetail(six.text_type): + """ + A string-like object that can additionally + """ + code = None + + def __new__(cls, string, code=None): + self = super(ErrorDetail, cls).__new__(cls, string) + self.code = code + return self class APIException(Exception): @@ -47,16 +81,35 @@ class APIException(Exception): """ status_code = status.HTTP_500_INTERNAL_SERVER_ERROR default_detail = _('A server error occurred.') + default_code = 'error' - def __init__(self, detail=None): - if detail is not None: - self.detail = force_text(detail) - else: - self.detail = force_text(self.default_detail) + def __init__(self, detail=None, code=None): + if detail is None: + detail = self.default_detail + if code is None: + code = self.default_code + + self.detail = _get_error_details(detail, code) def __str__(self): return self.detail + def get_codes(self): + """ + Return only the code part of the error details. + + Eg. {"name": ["required"]} + """ + return _get_codes(self.detail) + + def get_full_details(self): + """ + Return both the message & code parts of the error details. + + Eg. {"name": [{"message": "This field is required.", "code": "required"}]} + """ + return _get_full_details(self.detail) + # The recommended style for using `ValidationError` is to keep it namespaced # under `serializers`, in order to minimize potential confusion with Django's @@ -67,13 +120,21 @@ class APIException(Exception): class ValidationError(APIException): status_code = status.HTTP_400_BAD_REQUEST + default_detail = _('Invalid input.') + default_code = 'invalid' - def __init__(self, detail): - # For validation errors the 'detail' key is always required. - # The details should always be coerced to a list if not already. + def __init__(self, detail, code=None): + if detail is None: + detail = self.default_detail + if code is None: + code = self.default_code + + # For validation failures, we may collect may errors together, so the + # details should always be coerced to a list if not already. if not isinstance(detail, dict) and not isinstance(detail, list): detail = [detail] - self.detail = _force_text_recursive(detail) + + self.detail = _get_error_details(detail, code) def __str__(self): return six.text_type(self.detail) @@ -82,62 +143,63 @@ class ValidationError(APIException): class ParseError(APIException): status_code = status.HTTP_400_BAD_REQUEST default_detail = _('Malformed request.') + default_code = 'parse_error' class AuthenticationFailed(APIException): status_code = status.HTTP_401_UNAUTHORIZED default_detail = _('Incorrect authentication credentials.') + default_code = 'authentication_failed' class NotAuthenticated(APIException): status_code = status.HTTP_401_UNAUTHORIZED default_detail = _('Authentication credentials were not provided.') + default_code = 'not_authenticated' class PermissionDenied(APIException): status_code = status.HTTP_403_FORBIDDEN default_detail = _('You do not have permission to perform this action.') + default_code = 'permission_denied' class NotFound(APIException): status_code = status.HTTP_404_NOT_FOUND default_detail = _('Not found.') + default_code = 'not_found' class MethodNotAllowed(APIException): status_code = status.HTTP_405_METHOD_NOT_ALLOWED default_detail = _('Method "{method}" not allowed.') + default_code = '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).format(method=method) + def __init__(self, method, detail=None, code=None): + if detail is None: + detail = force_text(self.default_detail).format(method=method) + super(MethodNotAllowed, self).__init__(detail, code) class NotAcceptable(APIException): status_code = status.HTTP_406_NOT_ACCEPTABLE default_detail = _('Could not satisfy the request Accept header.') + default_code = 'not_acceptable' - def __init__(self, detail=None, available_renderers=None): - if detail is not None: - self.detail = force_text(detail) - else: - self.detail = force_text(self.default_detail) + def __init__(self, detail=None, code=None, available_renderers=None): self.available_renderers = available_renderers + super(NotAcceptable, self).__init__(detail, code) class UnsupportedMediaType(APIException): status_code = status.HTTP_415_UNSUPPORTED_MEDIA_TYPE default_detail = _('Unsupported media type "{media_type}" in request.') + default_code = 'unsupported_media_type' - 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).format( - media_type=media_type - ) + def __init__(self, media_type, detail=None, code=None): + if detail is None: + detail = force_text(self.default_detail).format(media_type=media_type) + super(UnsupportedMediaType, self).__init__(detail, code) class Throttled(APIException): @@ -145,12 +207,10 @@ class Throttled(APIException): default_detail = _('Request was throttled.') extra_detail_singular = 'Expected available in {wait} second.' extra_detail_plural = 'Expected available in {wait} seconds.' + default_code = 'throttled' - def __init__(self, wait=None, detail=None): - if detail is not None: - self.detail = force_text(detail) - else: - self.detail = force_text(self.default_detail) + def __init__(self, wait=None, detail=None, code=None): + super(Throttled, self).__init__(detail, code) if wait is None: self.wait = None diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 7f8391b8a..1894b064c 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -34,7 +34,7 @@ from rest_framework import ISO_8601 from rest_framework.compat import ( get_remote_field, unicode_repr, unicode_to_repr, value_from_object ) -from rest_framework.exceptions import ValidationError +from rest_framework.exceptions import ErrorDetail, ValidationError from rest_framework.settings import api_settings from rest_framework.utils import html, humanize_datetime, representation @@ -224,6 +224,18 @@ def iter_options(grouped_choices, cutoff=None, cutoff_text=None): yield Option(value='n/a', display_text=cutoff_text, disabled=True) +def get_error_detail(exc_info): + """ + Given a Django ValidationError, return a list of ErrorDetail, + with the `code` populated. + """ + code = getattr(exc_info, 'code', None) or 'invalid' + return [ + ErrorDetail(msg, code=code) + for msg in exc_info.messages + ] + + class CreateOnlyDefault(object): """ This class may be used to provide default values that are only used @@ -525,7 +537,7 @@ class Field(object): raise errors.extend(exc.detail) except DjangoValidationError as exc: - errors.extend(exc.messages) + errors.extend(get_error_detail(exc)) if errors: raise ValidationError(errors) @@ -563,7 +575,7 @@ class Field(object): msg = MISSING_ERROR_MESSAGE.format(class_name=class_name, key=key) raise AssertionError(msg) message_string = msg.format(**kwargs) - raise ValidationError(message_string) + raise ValidationError(message_string, code=key) @cached_property def root(self): diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 7e99d40b3..a6ed7d87e 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -291,32 +291,29 @@ class SerializerMetaclass(type): return super(SerializerMetaclass, cls).__new__(cls, name, bases, attrs) -def get_validation_error_detail(exc): +def as_serializer_error(exc): assert isinstance(exc, (ValidationError, DjangoValidationError)) if isinstance(exc, DjangoValidationError): - # Normally you should raise `serializers.ValidationError` - # inside your codebase, but we handle Django's validation - # exception class as well for simpler compat. - # Eg. Calling Model.clean() explicitly inside Serializer.validate() - return { - api_settings.NON_FIELD_ERRORS_KEY: list(exc.messages) - } - elif isinstance(exc.detail, dict): + detail = get_error_detail(exc) + else: + detail = exc.detail + + if isinstance(detail, dict): # If errors may be a dict we use the standard {key: list of values}. # Here we ensure that all the values are *lists* of errors. return { key: value if isinstance(value, (list, dict)) else [value] - for key, value in exc.detail.items() + for key, value in detail.items() } - elif isinstance(exc.detail, list): + elif isinstance(detail, list): # Errors raised as a list are non-field errors. return { - api_settings.NON_FIELD_ERRORS_KEY: exc.detail + api_settings.NON_FIELD_ERRORS_KEY: detail } # Errors raised as a string are non-field errors. return { - api_settings.NON_FIELD_ERRORS_KEY: [exc.detail] + api_settings.NON_FIELD_ERRORS_KEY: [detail] } @@ -410,7 +407,7 @@ class Serializer(BaseSerializer): value = self.validate(value) assert value is not None, '.validate() should return the validated data' except (ValidationError, DjangoValidationError) as exc: - raise ValidationError(detail=get_validation_error_detail(exc)) + raise ValidationError(detail=as_serializer_error(exc)) return value @@ -424,7 +421,7 @@ class Serializer(BaseSerializer): ) raise ValidationError({ api_settings.NON_FIELD_ERRORS_KEY: [message] - }) + }, code='invalid') ret = OrderedDict() errors = OrderedDict() @@ -440,7 +437,7 @@ class Serializer(BaseSerializer): except ValidationError as exc: errors[field.field_name] = exc.detail except DjangoValidationError as exc: - errors[field.field_name] = list(exc.messages) + errors[field.field_name] = get_error_detail(exc) except SkipField: pass else: @@ -564,7 +561,7 @@ class ListSerializer(BaseSerializer): value = self.validate(value) assert value is not None, '.validate() should return the validated data' except (ValidationError, DjangoValidationError) as exc: - raise ValidationError(detail=get_validation_error_detail(exc)) + raise ValidationError(detail=as_serializer_error(exc)) return value @@ -581,13 +578,13 @@ class ListSerializer(BaseSerializer): ) raise ValidationError({ api_settings.NON_FIELD_ERRORS_KEY: [message] - }) + }, code='not_a_list') if not self.allow_empty and len(data) == 0: message = self.error_messages['empty'] raise ValidationError({ api_settings.NON_FIELD_ERRORS_KEY: [message] - }) + }, code='empty') ret = [] errors = [] diff --git a/rest_framework/validators.py b/rest_framework/validators.py index 84af0b9d5..57f8bad53 100644 --- a/rest_framework/validators.py +++ b/rest_framework/validators.py @@ -80,7 +80,7 @@ class UniqueValidator(object): queryset = self.filter_queryset(value, queryset) queryset = self.exclude_current_instance(queryset) if qs_exists(queryset): - raise ValidationError(self.message) + raise ValidationError(self.message, code='unique') def __repr__(self): return unicode_to_repr('<%s(queryset=%s)>' % ( @@ -120,13 +120,13 @@ class UniqueTogetherValidator(object): if self.instance is not None: return - missing = { + missing_items = { field_name: self.missing_message for field_name in self.fields if field_name not in attrs } - if missing: - raise ValidationError(missing) + if missing_items: + raise ValidationError(missing_items, code='required') def filter_queryset(self, attrs, queryset): """ @@ -167,7 +167,8 @@ class UniqueTogetherValidator(object): ] if None not in checked_values and qs_exists(queryset): field_names = ', '.join(self.fields) - raise ValidationError(self.message.format(field_names=field_names)) + message = self.message.format(field_names=field_names) + raise ValidationError(message, code='unique') def __repr__(self): return unicode_to_repr('<%s(queryset=%s, fields=%s)>' % ( @@ -204,13 +205,13 @@ class BaseUniqueForValidator(object): The `UniqueForValidator` classes always force an implied 'required' state on the fields they are applied to. """ - missing = { + missing_items = { field_name: self.missing_message for field_name in [self.field, self.date_field] if field_name not in attrs } - if missing: - raise ValidationError(missing) + if missing_items: + raise ValidationError(missing_items, code='required') def filter_queryset(self, attrs, queryset): raise NotImplementedError('`filter_queryset` must be implemented.') @@ -231,7 +232,9 @@ class BaseUniqueForValidator(object): queryset = self.exclude_current_instance(attrs, queryset) if qs_exists(queryset): message = self.message.format(date_field=self.date_field) - raise ValidationError({self.field: message}) + raise ValidationError({ + self.field: message + }, code='unique') def __repr__(self): return unicode_to_repr('<%s(queryset=%s, field=%s, date_field=%s)>' % ( diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index cec050eb8..29703cb77 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -3,19 +3,39 @@ from __future__ import unicode_literals from django.test import TestCase from django.utils.translation import ugettext_lazy as _ -from rest_framework.exceptions import _force_text_recursive +from rest_framework.exceptions import ErrorDetail, _get_error_details class ExceptionTestCase(TestCase): - def test_force_text_recursive(self): + def test_get_error_details(self): - s = "sfdsfggiuytraetfdlklj" - self.assertEqual(_force_text_recursive(_(s)), s) - self.assertEqual(type(_force_text_recursive(_(s))), type(s)) + example = "string" + lazy_example = _(example) - self.assertEqual(_force_text_recursive({'a': _(s)})['a'], s) - self.assertEqual(type(_force_text_recursive({'a': _(s)})['a']), type(s)) + self.assertEqual( + _get_error_details(lazy_example), + example + ) + assert isinstance( + _get_error_details(lazy_example), + ErrorDetail + ) - self.assertEqual(_force_text_recursive([[_(s)]])[0][0], s) - self.assertEqual(type(_force_text_recursive([[_(s)]])[0][0]), type(s)) + self.assertEqual( + _get_error_details({'nested': lazy_example})['nested'], + example + ) + assert isinstance( + _get_error_details({'nested': lazy_example})['nested'], + ErrorDetail + ) + + self.assertEqual( + _get_error_details([[lazy_example]])[0][0], + example + ) + assert isinstance( + _get_error_details([[lazy_example]])[0][0], + ErrorDetail + ) diff --git a/tests/test_validation.py b/tests/test_validation.py index ab04d59e6..bc950dd22 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -60,7 +60,7 @@ class TestNestedValidationError(TestCase): } }) - self.assertEqual(serializers.get_validation_error_detail(e), { + self.assertEqual(serializers.as_serializer_error(e), { 'nested': { 'field': ['error'], } diff --git a/tests/test_validation_error.py b/tests/test_validation_error.py new file mode 100644 index 000000000..8e371a349 --- /dev/null +++ b/tests/test_validation_error.py @@ -0,0 +1,101 @@ +from django.test import TestCase + +from rest_framework import serializers, status +from rest_framework.decorators import api_view +from rest_framework.response import Response +from rest_framework.settings import api_settings +from rest_framework.test import APIRequestFactory +from rest_framework.views import APIView + +factory = APIRequestFactory() + + +class ExampleSerializer(serializers.Serializer): + char = serializers.CharField() + integer = serializers.IntegerField() + + +class ErrorView(APIView): + def get(self, request, *args, **kwargs): + ExampleSerializer(data={}).is_valid(raise_exception=True) + + +@api_view(['GET']) +def error_view(request): + ExampleSerializer(data={}).is_valid(raise_exception=True) + + +class TestValidationErrorWithFullDetails(TestCase): + def setUp(self): + self.DEFAULT_HANDLER = api_settings.EXCEPTION_HANDLER + + def exception_handler(exc, request): + data = exc.get_full_details() + return Response(data, status=status.HTTP_400_BAD_REQUEST) + + api_settings.EXCEPTION_HANDLER = exception_handler + + self.expected_response_data = { + 'char': [{ + 'message': 'This field is required.', + 'code': 'required', + }], + 'integer': [{ + 'message': 'This field is required.', + 'code': 'required' + }], + } + + def tearDown(self): + api_settings.EXCEPTION_HANDLER = self.DEFAULT_HANDLER + + def test_class_based_view_exception_handler(self): + view = ErrorView.as_view() + + request = factory.get('/', content_type='application/json') + response = view(request) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.data, self.expected_response_data) + + def test_function_based_view_exception_handler(self): + view = error_view + + request = factory.get('/', content_type='application/json') + response = view(request) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.data, self.expected_response_data) + + +class TestValidationErrorWithCodes(TestCase): + def setUp(self): + self.DEFAULT_HANDLER = api_settings.EXCEPTION_HANDLER + + def exception_handler(exc, request): + data = exc.get_codes() + return Response(data, status=status.HTTP_400_BAD_REQUEST) + + api_settings.EXCEPTION_HANDLER = exception_handler + + self.expected_response_data = { + 'char': ['required'], + 'integer': ['required'], + } + + def tearDown(self): + api_settings.EXCEPTION_HANDLER = self.DEFAULT_HANDLER + + def test_class_based_view_exception_handler(self): + view = ErrorView.as_view() + + request = factory.get('/', content_type='application/json') + response = view(request) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.data, self.expected_response_data) + + def test_function_based_view_exception_handler(self): + view = error_view + + request = factory.get('/', content_type='application/json') + response = view(request) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.data, self.expected_response_data) From aed4ed5e73638025ae3c9d591e2731b1e792a344 Mon Sep 17 00:00:00 2001 From: Akshay Sharma Date: Tue, 11 Oct 2016 15:29:00 +0530 Subject: [PATCH 131/175] Browsable API navbar gets overlapped by highlighted pagination item fix (#4547) --- rest_framework/static/rest_framework/css/bootstrap-tweaks.css | 1 - 1 file changed, 1 deletion(-) diff --git a/rest_framework/static/rest_framework/css/bootstrap-tweaks.css b/rest_framework/static/rest_framework/css/bootstrap-tweaks.css index 17085b49d..c2fcb303d 100644 --- a/rest_framework/static/rest_framework/css/bootstrap-tweaks.css +++ b/rest_framework/static/rest_framework/css/bootstrap-tweaks.css @@ -32,7 +32,6 @@ a single block in the template. position: fixed; left: 0; top: 0; - z-index: 3; } .navbar { From d0b3b6470a4f16d8256d0db5ed1be0f5a65dc0dc Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 11 Oct 2016 11:07:40 +0100 Subject: [PATCH 132/175] Fix prefetch_related updates. (#4553) --- rest_framework/mixins.py | 7 ++++++ tests/test_prefetch_related.py | 41 ++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tests/test_prefetch_related.py diff --git a/rest_framework/mixins.py b/rest_framework/mixins.py index 1104aa29c..47a4923a1 100644 --- a/rest_framework/mixins.py +++ b/rest_framework/mixins.py @@ -68,6 +68,13 @@ class UpdateModelMixin(object): serializer = self.get_serializer(instance, data=request.data, partial=partial) serializer.is_valid(raise_exception=True) self.perform_update(serializer) + + if getattr(instance, '_prefetched_objects_cache', None): + # If 'prefetch_related' has been applied to a queryset, we need to + # refresh the instance from the database. + instance = self.get_object() + serializer = self.get_serializer(instance) + return Response(serializer.data) def perform_update(self, serializer): diff --git a/tests/test_prefetch_related.py b/tests/test_prefetch_related.py new file mode 100644 index 000000000..fc697adc1 --- /dev/null +++ b/tests/test_prefetch_related.py @@ -0,0 +1,41 @@ +from django.contrib.auth.models import Group, User +from django.test import TestCase + +from rest_framework import generics, serializers +from rest_framework.test import APIRequestFactory + +factory = APIRequestFactory() + + +class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ('id', 'username', 'email', 'groups') + + +class UserUpdate(generics.UpdateAPIView): + queryset = User.objects.all().prefetch_related('groups') + serializer_class = UserSerializer + + +class TestPrefetchRelatedUpdates(TestCase): + def setUp(self): + self.user = User.objects.create(username='tom', email='tom@example.com') + self.groups = [Group.objects.create(name='a'), Group.objects.create(name='b')] + self.user.groups = self.groups + self.user.save() + + def test_prefetch_related_updates(self): + view = UserUpdate.as_view() + pk = self.user.pk + groups_pk = self.groups[0].pk + request = factory.put('/', {'username': 'new', 'groups': [groups_pk]}, format='json') + response = view(request, pk=pk) + assert User.objects.get(pk=pk).groups.count() == 1 + expected = { + 'id': pk, + 'username': 'new', + 'groups': [1], + 'email': 'tom@example.com' + } + assert response.data == expected From 7f29cfc931110078d5b915fe0001a6ce06998c86 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 11 Oct 2016 12:18:00 +0100 Subject: [PATCH 133/175] Lazy hyperlink names (#4554) --- rest_framework/relations.py | 17 ++++--- rest_framework/templatetags/rest_framework.py | 3 +- tests/test_lazy_hyperlinks.py | 49 +++++++++++++++++++ 3 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 tests/test_lazy_hyperlinks.py diff --git a/rest_framework/relations.py b/rest_framework/relations.py index c6eb92a24..c1898d0d8 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -37,14 +37,21 @@ class Hyperlink(six.text_type): We use this for hyperlinked URLs that may render as a named link in some contexts, or render as a plain URL in others. """ - def __new__(self, url, name): + def __new__(self, url, obj): ret = six.text_type.__new__(self, url) - ret.name = name + ret.obj = obj return ret def __getnewargs__(self): return(str(self), self.name,) + @property + def name(self): + # This ensures that we only called `__str__` lazily, + # as in some cases calling __str__ on a model instances *might* + # involve a database lookup. + return six.text_type(self.obj) + is_hyperlink = True @@ -303,9 +310,6 @@ class HyperlinkedRelatedField(RelatedField): kwargs = {self.lookup_url_kwarg: lookup_value} return self.reverse(view_name, kwargs=kwargs, request=request, format=format) - def get_name(self, obj): - return six.text_type(obj) - def to_internal_value(self, data): request = self.context.get('request', None) try: @@ -384,8 +388,7 @@ class HyperlinkedRelatedField(RelatedField): if url is None: return None - name = self.get_name(value) - return Hyperlink(url, name) + return Hyperlink(url, value) class HyperlinkedIdentityField(HyperlinkedRelatedField): diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index c1c8a5396..cfba054f6 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -135,7 +135,8 @@ def add_class(value, css_class): @register.filter def format_value(value): if getattr(value, 'is_hyperlink', False): - return mark_safe('%s' % (value, escape(value.name))) + name = six.text_type(value.obj) + return mark_safe('%s' % (value, escape(name))) if value is None or isinstance(value, bool): return mark_safe('%s' % {True: 'true', False: 'false', None: 'null'}[value]) elif isinstance(value, list): diff --git a/tests/test_lazy_hyperlinks.py b/tests/test_lazy_hyperlinks.py new file mode 100644 index 000000000..cf3ee735f --- /dev/null +++ b/tests/test_lazy_hyperlinks.py @@ -0,0 +1,49 @@ +from django.conf.urls import url +from django.db import models +from django.test import TestCase, override_settings + +from rest_framework import serializers +from rest_framework.renderers import JSONRenderer +from rest_framework.templatetags.rest_framework import format_value + +str_called = False + + +class Example(models.Model): + text = models.CharField(max_length=100) + + def __str__(self): + global str_called + str_called = True + return 'An example' + + +class ExampleSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = Example + fields = ('url', 'id', 'text') + + +def dummy_view(request): + pass + + +urlpatterns = [ + url(r'^example/(?P[0-9]+)/$', dummy_view, name='example-detail'), +] + + +@override_settings(ROOT_URLCONF='tests.test_lazy_hyperlinks') +class TestLazyHyperlinkNames(TestCase): + def setUp(self): + self.example = Example.objects.create(text='foo') + + def test_lazy_hyperlink_names(self): + global str_called + context = {'request': None} + serializer = ExampleSerializer(self.example, context=context) + JSONRenderer().render(serializer.data) + assert not str_called + hyperlink_string = format_value(serializer.data['url']) + assert hyperlink_string == 'An example' + assert str_called From 02fcf6a3346a9d47a6c4297fd562708feb27ed89 Mon Sep 17 00:00:00 2001 From: SerenityCode Date: Wed, 12 Oct 2016 09:46:59 +0100 Subject: [PATCH 134/175] Use field name instead of source when generating docs (#4559) --- rest_framework/schemas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py index 69698db0e..af861426c 100644 --- a/rest_framework/schemas.py +++ b/rest_framework/schemas.py @@ -477,7 +477,7 @@ class SchemaGenerator(object): required = field.required and method != 'PATCH' description = force_text(field.help_text) if field.help_text else '' field = coreapi.Field( - name=field.source, + name=field.field_name, location='form', required=required, description=description, From 11a89ebff42773452d2d6382ef96e8b33a24424c Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 12 Oct 2016 10:02:21 +0100 Subject: [PATCH 135/175] Removous erronous duplicate Danish translation file (#4563) --- .../locale/da_DK/LC_MESSAGES/django.mo | Bin 529 -> 0 bytes .../locale/da_DK/LC_MESSAGES/django.po | 439 ------------------ 2 files changed, 439 deletions(-) delete mode 100644 rest_framework/locale/da_DK/LC_MESSAGES/django.mo delete mode 100644 rest_framework/locale/da_DK/LC_MESSAGES/django.po diff --git a/rest_framework/locale/da_DK/LC_MESSAGES/django.mo b/rest_framework/locale/da_DK/LC_MESSAGES/django.mo deleted file mode 100644 index f34583e4c20fbd84e69fe3c25847ea9bc43df310..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 529 zcmZut!A=`75T$BQd+fP~snkOvnCvd4ZHTrc1ZXTzOA=@ zOJ@TL7oPNFd;G@FZ~lG$^t{LT!2H7e#{9@UV73@A|MKMzUR%u$b7$`<3wV{|Z*pE+ zBVlr{OkrU$y-i{1R84E^9-;*`)&-pTLg#p~Dm@+(Q>SWeAOo}=- BpPB#w diff --git a/rest_framework/locale/da_DK/LC_MESSAGES/django.po b/rest_framework/locale/da_DK/LC_MESSAGES/django.po deleted file mode 100644 index bb512e284..000000000 --- a/rest_framework/locale/da_DK/LC_MESSAGES/django.po +++ /dev/null @@ -1,439 +0,0 @@ -# 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: 2016-07-12 16:13+0100\n" -"PO-Revision-Date: 2016-07-12 15:14+0000\n" -"Last-Translator: Thomas Christie \n" -"Language-Team: Danish (Denmark) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/da_DK/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Language: da_DK\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: authentication.py:73 -msgid "Invalid basic header. No credentials provided." -msgstr "" - -#: authentication.py:76 -msgid "Invalid basic header. Credentials string should not contain spaces." -msgstr "" - -#: authentication.py:82 -msgid "Invalid basic header. Credentials not correctly base64 encoded." -msgstr "" - -#: authentication.py:99 -msgid "Invalid username/password." -msgstr "" - -#: authentication.py:102 authentication.py:198 -msgid "User inactive or deleted." -msgstr "" - -#: authentication.py:176 -msgid "Invalid token header. No credentials provided." -msgstr "" - -#: authentication.py:179 -msgid "Invalid token header. Token string should not contain spaces." -msgstr "" - -#: authentication.py:185 -msgid "" -"Invalid token header. Token string should not contain invalid characters." -msgstr "" - -#: authentication.py:195 -msgid "Invalid token." -msgstr "" - -#: authtoken/apps.py:7 -msgid "Auth Token" -msgstr "" - -#: authtoken/models.py:15 -msgid "Key" -msgstr "" - -#: authtoken/models.py:18 -msgid "User" -msgstr "" - -#: authtoken/models.py:20 -msgid "Created" -msgstr "" - -#: authtoken/models.py:29 -msgid "Token" -msgstr "" - -#: authtoken/models.py:30 -msgid "Tokens" -msgstr "" - -#: authtoken/serializers.py:8 -msgid "Username" -msgstr "" - -#: authtoken/serializers.py:9 -msgid "Password" -msgstr "" - -#: authtoken/serializers.py:20 -msgid "User account is disabled." -msgstr "" - -#: authtoken/serializers.py:23 -msgid "Unable to log in with provided credentials." -msgstr "" - -#: authtoken/serializers.py:26 -msgid "Must include \"username\" and \"password\"." -msgstr "" - -#: exceptions.py:49 -msgid "A server error occurred." -msgstr "" - -#: exceptions.py:84 -msgid "Malformed request." -msgstr "" - -#: exceptions.py:89 -msgid "Incorrect authentication credentials." -msgstr "" - -#: exceptions.py:94 -msgid "Authentication credentials were not provided." -msgstr "" - -#: exceptions.py:99 -msgid "You do not have permission to perform this action." -msgstr "" - -#: exceptions.py:104 views.py:81 -msgid "Not found." -msgstr "" - -#: exceptions.py:109 -msgid "Method \"{method}\" not allowed." -msgstr "" - -#: exceptions.py:120 -msgid "Could not satisfy the request Accept header." -msgstr "" - -#: exceptions.py:132 -msgid "Unsupported media type \"{media_type}\" in request." -msgstr "" - -#: exceptions.py:145 -msgid "Request was throttled." -msgstr "" - -#: fields.py:269 relations.py:206 relations.py:239 validators.py:98 -#: validators.py:181 -msgid "This field is required." -msgstr "" - -#: fields.py:270 -msgid "This field may not be null." -msgstr "" - -#: fields.py:608 fields.py:639 -msgid "\"{input}\" is not a valid boolean." -msgstr "" - -#: fields.py:674 -msgid "This field may not be blank." -msgstr "" - -#: fields.py:675 fields.py:1675 -msgid "Ensure this field has no more than {max_length} characters." -msgstr "" - -#: fields.py:676 -msgid "Ensure this field has at least {min_length} characters." -msgstr "" - -#: fields.py:713 -msgid "Enter a valid email address." -msgstr "" - -#: fields.py:724 -msgid "This value does not match the required pattern." -msgstr "" - -#: fields.py:735 -msgid "" -"Enter a valid \"slug\" consisting of letters, numbers, underscores or " -"hyphens." -msgstr "" - -#: fields.py:747 -msgid "Enter a valid URL." -msgstr "" - -#: fields.py:760 -msgid "\"{value}\" is not a valid UUID." -msgstr "" - -#: fields.py:796 -msgid "Enter a valid IPv4 or IPv6 address." -msgstr "" - -#: fields.py:821 -msgid "A valid integer is required." -msgstr "" - -#: fields.py:822 fields.py:857 fields.py:891 -msgid "Ensure this value is less than or equal to {max_value}." -msgstr "" - -#: fields.py:823 fields.py:858 fields.py:892 -msgid "Ensure this value is greater than or equal to {min_value}." -msgstr "" - -#: fields.py:824 fields.py:859 fields.py:896 -msgid "String value too large." -msgstr "" - -#: fields.py:856 fields.py:890 -msgid "A valid number is required." -msgstr "" - -#: fields.py:893 -msgid "Ensure that there are no more than {max_digits} digits in total." -msgstr "" - -#: fields.py:894 -msgid "" -"Ensure that there are no more than {max_decimal_places} decimal places." -msgstr "" - -#: fields.py:895 -msgid "" -"Ensure that there are no more than {max_whole_digits} digits before the " -"decimal point." -msgstr "" - -#: fields.py:1025 -msgid "Datetime has wrong format. Use one of these formats instead: {format}." -msgstr "" - -#: fields.py:1026 -msgid "Expected a datetime but got a date." -msgstr "" - -#: fields.py:1103 -msgid "Date has wrong format. Use one of these formats instead: {format}." -msgstr "" - -#: fields.py:1104 -msgid "Expected a date but got a datetime." -msgstr "" - -#: fields.py:1170 -msgid "Time has wrong format. Use one of these formats instead: {format}." -msgstr "" - -#: fields.py:1232 -msgid "Duration has wrong format. Use one of these formats instead: {format}." -msgstr "" - -#: fields.py:1251 fields.py:1300 -msgid "\"{input}\" is not a valid choice." -msgstr "" - -#: fields.py:1254 relations.py:71 relations.py:441 -msgid "More than {count} items..." -msgstr "" - -#: fields.py:1301 fields.py:1448 relations.py:437 serializers.py:524 -msgid "Expected a list of items but got type \"{input_type}\"." -msgstr "" - -#: fields.py:1302 -msgid "This selection may not be empty." -msgstr "" - -#: fields.py:1339 -msgid "\"{input}\" is not a valid path choice." -msgstr "" - -#: fields.py:1358 -msgid "No file was submitted." -msgstr "" - -#: fields.py:1359 -msgid "" -"The submitted data was not a file. Check the encoding type on the form." -msgstr "" - -#: fields.py:1360 -msgid "No filename could be determined." -msgstr "" - -#: fields.py:1361 -msgid "The submitted file is empty." -msgstr "" - -#: fields.py:1362 -msgid "" -"Ensure this filename has at most {max_length} characters (it has {length})." -msgstr "" - -#: fields.py:1410 -msgid "" -"Upload a valid image. The file you uploaded was either not an image or a " -"corrupted image." -msgstr "" - -#: fields.py:1449 relations.py:438 serializers.py:525 -msgid "This list may not be empty." -msgstr "" - -#: fields.py:1502 -msgid "Expected a dictionary of items but got type \"{input_type}\"." -msgstr "" - -#: fields.py:1549 -msgid "Value must be valid JSON." -msgstr "" - -#: filters.py:36 templates/rest_framework/filters/django_filter.html:5 -msgid "Submit" -msgstr "" - -#: filters.py:336 -msgid "ascending" -msgstr "" - -#: filters.py:337 -msgid "descending" -msgstr "" - -#: pagination.py:193 -msgid "Invalid page." -msgstr "" - -#: pagination.py:427 -msgid "Invalid cursor" -msgstr "" - -#: relations.py:207 -msgid "Invalid pk \"{pk_value}\" - object does not exist." -msgstr "" - -#: relations.py:208 -msgid "Incorrect type. Expected pk value, received {data_type}." -msgstr "" - -#: relations.py:240 -msgid "Invalid hyperlink - No URL match." -msgstr "" - -#: relations.py:241 -msgid "Invalid hyperlink - Incorrect URL match." -msgstr "" - -#: relations.py:242 -msgid "Invalid hyperlink - Object does not exist." -msgstr "" - -#: relations.py:243 -msgid "Incorrect type. Expected URL string, received {data_type}." -msgstr "" - -#: relations.py:401 -msgid "Object with {slug_name}={value} does not exist." -msgstr "" - -#: relations.py:402 -msgid "Invalid value." -msgstr "" - -#: serializers.py:326 -msgid "Invalid data. Expected a dictionary, but got {datatype}." -msgstr "" - -#: templates/rest_framework/admin.html:116 -#: templates/rest_framework/base.html:128 -msgid "Filters" -msgstr "" - -#: templates/rest_framework/filters/django_filter.html:2 -#: templates/rest_framework/filters/django_filter_crispyforms.html:4 -msgid "Field filters" -msgstr "" - -#: templates/rest_framework/filters/ordering.html:3 -msgid "Ordering" -msgstr "" - -#: templates/rest_framework/filters/search.html:2 -msgid "Search" -msgstr "" - -#: templates/rest_framework/horizontal/radio.html:2 -#: templates/rest_framework/inline/radio.html:2 -#: templates/rest_framework/vertical/radio.html:2 -msgid "None" -msgstr "" - -#: templates/rest_framework/horizontal/select_multiple.html:2 -#: templates/rest_framework/inline/select_multiple.html:2 -#: templates/rest_framework/vertical/select_multiple.html:2 -msgid "No items to select." -msgstr "" - -#: validators.py:43 -msgid "This field must be unique." -msgstr "" - -#: validators.py:97 -msgid "The fields {field_names} must make a unique set." -msgstr "" - -#: validators.py:245 -msgid "This field must be unique for the \"{date_field}\" date." -msgstr "" - -#: validators.py:260 -msgid "This field must be unique for the \"{date_field}\" month." -msgstr "" - -#: validators.py:273 -msgid "This field must be unique for the \"{date_field}\" year." -msgstr "" - -#: versioning.py:42 -msgid "Invalid version in \"Accept\" header." -msgstr "" - -#: versioning.py:73 -msgid "Invalid version in URL path." -msgstr "" - -#: versioning.py:115 -msgid "Invalid version in URL path. Does not match any version namespace." -msgstr "" - -#: versioning.py:147 -msgid "Invalid version in hostname." -msgstr "" - -#: versioning.py:169 -msgid "Invalid version in query parameter." -msgstr "" - -#: views.py:88 -msgid "Permission denied." -msgstr "" From 4c9b14bd972f1c74fc400297be5ecd1a1e0317f0 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 12 Oct 2016 10:13:46 +0100 Subject: [PATCH 136/175] Add --minimum-perc to transifex pull command [ci skip] --- docs/topics/project-management.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/topics/project-management.md b/docs/topics/project-management.md index e84f2acfb..37e5c6882 100644 --- a/docs/topics/project-management.md +++ b/docs/topics/project-management.md @@ -6,7 +6,7 @@ This document outlines our project management processes for REST framework. -The aim is to ensure that the project has a high +The aim is to ensure that the project has a high ["bus factor"][bus-factor], and can continue to remain well supported for the foreseeable future. Suggestions for improvements to our process are welcome. --- @@ -38,27 +38,27 @@ Members of the maintenance team will be added as collaborators to the repository The following template should be used for the description of the issue, and serves as the formal process for selecting the team. This issue is for determining the maintenance team for the *** period. - + Please see the [Project management](http://www.django-rest-framework.org/topics/project-management/) section of our documentation for more details. - + --- - + #### Renewing existing members. - + The following people are the current maintenance team. Please checkmark your name if you wish to continue to have write permission on the repository for the *** period. - + - [ ] @*** - [ ] @*** - [ ] @*** - [ ] @*** - [ ] @*** - + --- - + #### New members. - + If you wish to be considered for this or a future date, please comment against this or subsequent issues. - + To modify this process for future maintenance cycles make a pull request to the [project management](http://www.django-rest-framework.org/topics/project-management/) documentation. #### Responsibilities of team members @@ -116,7 +116,7 @@ The following template should be used for the description of the issue, and serv - [ ] Make a release announcement on the [discussion group](https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework). - [ ] Make a release announcement on twitter. - [ ] Close the milestone on GitHub. - + To modify this process for future releases make a pull request to the [project management](http://www.django-rest-framework.org/topics/project-management/) documentation. When pushing the release to PyPI ensure that your environment has been installed from our development `requirement.txt`, so that documentation and PyPI installs are consistently being built against a pinned set of packages. @@ -165,7 +165,7 @@ Here's how differences between the old and new source files will be handled: 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 + tx pull -a --minimum-perc 10 cd rest_framework # 4. Compile the binary .mo files for all supported languages. django-admin.py compilemessages From b4199704316bd2d67281cbc3cf946eab9bded407 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 12 Oct 2016 10:47:17 +0100 Subject: [PATCH 137/175] Handle empty data with serializer (#4564) --- rest_framework/serializers.py | 10 ++++++++++ tests/test_serializer.py | 6 ++++++ tests/test_serializer_nested.py | 6 ++++++ 3 files changed, 22 insertions(+) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index a6ed7d87e..02c21da15 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -507,6 +507,11 @@ class Serializer(BaseSerializer): @property def errors(self): ret = super(Serializer, self).errors + if isinstance(ret, list) and len(ret) == 1 and ret[0].code == 'null': + # Edge case. Provide a more descriptive error than + # "this field may not be null", when no data is passed. + detail = ErrorDetail('No data provided', code='null') + ret = {api_settings.NON_FIELD_ERRORS_KEY: [detail]} return ReturnDict(ret, serializer=self) @@ -700,6 +705,11 @@ class ListSerializer(BaseSerializer): @property def errors(self): ret = super(ListSerializer, self).errors + if isinstance(ret, list) and len(ret) == 1 and ret[0].code == 'null': + # Edge case. Provide a more descriptive error than + # "this field may not be null", when no data is passed. + detail = ErrorDetail('No data provided', code='null') + ret = {api_settings.NON_FIELD_ERRORS_KEY: [detail]} if isinstance(ret, dict): return ReturnDict(ret, serializer=self) return ReturnList(ret, serializer=self) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index bd9ef9500..a2817f6a4 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -62,6 +62,12 @@ class TestSerializer: with pytest.raises(AssertionError): serializer.save() + def test_validate_none_data(self): + data = None + serializer = self.Serializer(data=data) + assert not serializer.is_valid() + assert serializer.errors == {'non_field_errors': ['No data provided']} + class TestValidateMethod: def test_non_field_error_validate_method(self): diff --git a/tests/test_serializer_nested.py b/tests/test_serializer_nested.py index 133600399..efb671918 100644 --- a/tests/test_serializer_nested.py +++ b/tests/test_serializer_nested.py @@ -41,6 +41,12 @@ class TestNestedSerializer: serializer = self.Serializer() assert serializer.data == expected_data + def test_nested_serialize_no_data(self): + data = None + serializer = self.Serializer(data=data) + assert not serializer.is_valid() + assert serializer.errors == {'non_field_errors': ['No data provided']} + class TestNotRequiredNestedSerializer: def setup(self): From 26e51ecd6c0805770615e768e46ecc6716872670 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 12 Oct 2016 14:04:10 +0100 Subject: [PATCH 138/175] When HTML form input is used with JSONField, treat the input as a JSON encoded string, not a JSON primative. (#4565) --- rest_framework/fields.py | 14 +++++++++++++- tests/test_fields.py | 13 +++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 1894b064c..e48285005 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1594,9 +1594,21 @@ class JSONField(Field): self.binary = kwargs.pop('binary', False) super(JSONField, self).__init__(*args, **kwargs) + def get_value(self, dictionary): + if html.is_html_input(dictionary) and self.field_name in dictionary: + # When HTML form input is used, mark up the input + # as being a JSON string, rather than a JSON primative. + class JSONString(six.text_type): + def __new__(self, value): + ret = six.text_type.__new__(self, value) + ret.is_json_string = True + return ret + return JSONString(dictionary[self.field_name]) + return dictionary.get(self.field_name, empty) + def to_internal_value(self, data): try: - if self.binary: + if self.binary or getattr(data, 'is_json_string', False): if isinstance(data, six.binary_type): data = data.decode('utf-8') return json.loads(data) diff --git a/tests/test_fields.py b/tests/test_fields.py index c271afa9e..6fea249ba 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1756,6 +1756,19 @@ class TestJSONField(FieldValues): ] field = serializers.JSONField() + def test_html_input_as_json_string(self): + """ + HTML inputs should be treated as a serialized JSON string. + """ + class TestSerializer(serializers.Serializer): + config = serializers.JSONField() + + data = QueryDict(mutable=True) + data.update({'config': '{"a":1}'}) + serializer = TestSerializer(data=data) + assert serializer.is_valid() + assert serializer.validated_data == {'config': {"a": 1}} + class TestBinaryJSONField(FieldValues): """ From 5677d063d8027bcacba07c203b7772816b78fb47 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 12 Oct 2016 15:46:24 +0100 Subject: [PATCH 139/175] Do not treat empty non-form input as HTML. (#4566) --- rest_framework/request.py | 5 ++++- tests/test_testing.py | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/rest_framework/request.py b/rest_framework/request.py index 0a827728a..7121689d2 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -299,7 +299,10 @@ class Request(object): stream = None if stream is None or media_type is None: - empty_data = QueryDict('', encoding=self._request._encoding) + if media_type and not is_form_media_type(media_type): + empty_data = QueryDict('', encoding=self._request._encoding) + else: + empty_data = {} empty_files = MultiValueDict() return (empty_data, empty_files) diff --git a/tests/test_testing.py b/tests/test_testing.py index 6683ae6ed..e6a47d914 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -8,6 +8,7 @@ from django.contrib.auth.models import User from django.shortcuts import redirect from django.test import TestCase, override_settings +from rest_framework import fields, serializers from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework.test import ( @@ -37,10 +38,22 @@ def redirect_view(request): return redirect('/view/') +class BasicSerializer(serializers.Serializer): + flag = fields.BooleanField(default=lambda: True) + + +@api_view(['POST']) +def post_view(request): + serializer = BasicSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + return Response(serializer.validated_data) + + urlpatterns = [ url(r'^view/$', view), url(r'^session-view/$', session_view), url(r'^redirect-view/$', redirect_view), + url(r'^post-view/$', post_view) ] @@ -181,6 +194,15 @@ class TestAPITestClient(TestCase): path='/view/', data={'valid': 123, 'invalid': {'a': 123}} ) + def test_empty_post_uses_default_boolean_value(self): + response = self.client.post( + '/post-view/', + data=None, + content_type='application/json' + ) + self.assertEqual(response.status_code, 200, response.content) + self.assertEqual(response.data, {"flag": True}) + class TestAPIRequestFactory(TestCase): def test_csrf_exempt_by_default(self): From 2519ce9128e4e870e28d77c0f388e3d2deb130c7 Mon Sep 17 00:00:00 2001 From: Alexey Evseev Date: Wed, 12 Oct 2016 18:09:45 +0300 Subject: [PATCH 140/175] Fix schema generation with custom page_size pagination param (#4567) --- rest_framework/pagination.py | 4 ++-- tests/test_schemas.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 6b539b900..708da29cd 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -290,9 +290,9 @@ class PageNumberPagination(BasePagination): coreapi.Field(name=self.page_query_param, required=False, location='query') ] if self.page_size_query_param is not None: - fields.append([ + fields.append( coreapi.Field(name=self.page_size_query_param, required=False, location='query') - ]) + ) return fields diff --git a/tests/test_schemas.py b/tests/test_schemas.py index c43fc1eff..7188087c4 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -20,6 +20,7 @@ class MockUser(object): class ExamplePagination(pagination.PageNumberPagination): page_size = 100 + page_size_query_param = 'page_size' class EmptySerializer(serializers.Serializer): @@ -64,7 +65,6 @@ class ExampleViewSet(ModelViewSet): assert self.action return super(ExampleViewSet, self).get_serializer(*args, **kwargs) - if coreapi: schema_view = get_schema_view(title='Example API') else: @@ -96,6 +96,7 @@ class TestRouterGeneratedSchema(TestCase): action='get', fields=[ coreapi.Field('page', required=False, location='query'), + coreapi.Field('page_size', required=False, location='query'), coreapi.Field('ordering', required=False, location='query') ] ), @@ -136,6 +137,7 @@ class TestRouterGeneratedSchema(TestCase): action='get', fields=[ coreapi.Field('page', required=False, location='query'), + coreapi.Field('page_size', required=False, location='query'), coreapi.Field('ordering', required=False, location='query') ] ), From 88c6c380c54d163d140485c8b53c923b7ec539e3 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 12 Oct 2016 16:51:01 +0100 Subject: [PATCH 141/175] Use field.source to perform check for writable nested field, not key (#4568) --- rest_framework/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 02c21da15..f13c92a44 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -744,8 +744,8 @@ def raise_errors_on_nested_writes(method_name, serializer, validated_data): # profile = ProfileSerializer() assert not any( isinstance(field, BaseSerializer) and - (key in validated_data) and - isinstance(validated_data[key], (list, dict)) + (field.source in validated_data) and + isinstance(validated_data[field.source], (list, dict)) for key, field in serializer.fields.items() ), ( 'The `.{method_name}()` method does not support writable nested ' From 8d0a91b002d7d18a1526e80e9cea0702e18be90e Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 13 Oct 2016 12:43:43 +0100 Subject: [PATCH 142/175] Fix 3674 (#4571) Handle ModelSerializer case for relationships to models with custom pk. --- rest_framework/serializers.py | 2 +- rest_framework/utils/field_mapping.py | 2 +- rest_framework/utils/model_meta.py | 15 ++++-- tests/test_model_serializer.py | 68 +++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 7 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index f13c92a44..098c3cd23 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -1167,7 +1167,7 @@ class ModelSerializer(Serializer): field_kwargs = get_relation_kwargs(field_name, relation_info) to_field = field_kwargs.pop('to_field', None) - if to_field and not relation_info.related_model._meta.get_field(to_field).primary_key: + if to_field and not relation_info.reverse and not relation_info.related_model._meta.get_field(to_field).primary_key: field_kwargs['slug_field'] = to_field field_class = self.serializer_related_to_field diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index 64df9a08e..29005f6b7 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -238,7 +238,7 @@ def get_relation_kwargs(field_name, relation_info): """ Creates a default instance of a flat relational field. """ - model_field, related_model, to_many, to_field, has_through_model = relation_info + model_field, related_model, to_many, to_field, has_through_model, reverse = relation_info kwargs = { 'queryset': related_model._default_manager, 'view_name': get_detail_view_name(related_model) diff --git a/rest_framework/utils/model_meta.py b/rest_framework/utils/model_meta.py index 94aa46e72..3e3e434e6 100644 --- a/rest_framework/utils/model_meta.py +++ b/rest_framework/utils/model_meta.py @@ -23,7 +23,8 @@ RelationInfo = namedtuple('RelationInfo', [ 'related_model', 'to_many', 'to_field', - 'has_through_model' + 'has_through_model', + 'reverse' ]) @@ -81,7 +82,8 @@ def _get_forward_relationships(opts): related_model=get_related_model(field), to_many=False, to_field=_get_to_field(field), - has_through_model=False + has_through_model=False, + reverse=False ) # Deal with forward many-to-many relationships. @@ -94,7 +96,8 @@ def _get_forward_relationships(opts): to_field=None, has_through_model=( not get_remote_field(field).through._meta.auto_created - ) + ), + reverse=False ) return forward_relations @@ -118,7 +121,8 @@ def _get_reverse_relationships(opts): related_model=related, to_many=get_remote_field(relation.field).multiple, to_field=_get_to_field(relation.field), - has_through_model=False + has_through_model=False, + reverse=True ) # Deal with reverse many-to-many relationships. @@ -135,7 +139,8 @@ def _get_reverse_relationships(opts): has_through_model=( (getattr(get_remote_field(relation.field), 'through', None) is not None) and not get_remote_field(relation.field).through._meta.auto_created - ) + ), + reverse=True ) return reverse_relations diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index cd9b2dfc3..06848302f 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -89,6 +89,15 @@ class ChoicesModel(models.Model): choices_field_with_nonstandard_args = models.DecimalField(max_digits=3, decimal_places=1, choices=DECIMAL_CHOICES, verbose_name='A label') +class Issue3674ParentModel(models.Model): + title = models.CharField(max_length=64) + + +class Issue3674ChildModel(models.Model): + parent = models.ForeignKey(Issue3674ParentModel, related_name='children') + value = models.CharField(primary_key=True, max_length=64) + + class TestModelSerializer(TestCase): def test_create_method(self): class TestSerializer(serializers.ModelSerializer): @@ -996,3 +1005,62 @@ class TestUniquenessOverride(TestCase): fields = TestSerializer().fields self.assertFalse(fields['field_1'].required) self.assertTrue(fields['field_2'].required) + + +class Issue3674Test(TestCase): + def test_nonPK_foreignkey_model_serializer(self): + class TestParentModel(models.Model): + title = models.CharField(max_length=64) + + class TestChildModel(models.Model): + parent = models.ForeignKey(TestParentModel, related_name='children') + value = models.CharField(primary_key=True, max_length=64) + + class TestChildModelSerializer(serializers.ModelSerializer): + class Meta: + model = TestChildModel + fields = ('value', 'parent') + + class TestParentModelSerializer(serializers.ModelSerializer): + class Meta: + model = TestParentModel + fields = ('id', 'title', 'children') + + parent_expected = dedent(""" + TestParentModelSerializer(): + id = IntegerField(label='ID', read_only=True) + title = CharField(max_length=64) + children = PrimaryKeyRelatedField(many=True, queryset=TestChildModel.objects.all()) + """) + self.assertEqual(unicode_repr(TestParentModelSerializer()), parent_expected) + + child_expected = dedent(""" + TestChildModelSerializer(): + value = CharField(max_length=64, validators=[]) + parent = PrimaryKeyRelatedField(queryset=TestParentModel.objects.all()) + """) + self.assertEqual(unicode_repr(TestChildModelSerializer()), child_expected) + + def test_nonID_PK_foreignkey_model_serializer(self): + + class TestChildModelSerializer(serializers.ModelSerializer): + class Meta: + model = Issue3674ChildModel + fields = ('value', 'parent') + + class TestParentModelSerializer(serializers.ModelSerializer): + class Meta: + model = Issue3674ParentModel + fields = ('id', 'title', 'children') + + parent = Issue3674ParentModel.objects.create(title='abc') + child = Issue3674ChildModel.objects.create(value='def', parent=parent) + + parent_serializer = TestParentModelSerializer(parent) + child_serializer = TestChildModelSerializer(child) + + parent_expected = {'children': ['def'], 'id': 1, 'title': 'abc'} + self.assertEqual(parent_serializer.data, parent_expected) + + child_expected = {'parent': 1, 'value': 'def'} + self.assertEqual(child_serializer.data, child_expected) From de08f28a9185c09667eace53b637b1cb4529c695 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 13 Oct 2016 14:21:23 +0100 Subject: [PATCH 143/175] Test one to one with inheritance (#4575) --- tests/test_one_to_one_with_inheritance.py | 46 +++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/test_one_to_one_with_inheritance.py diff --git a/tests/test_one_to_one_with_inheritance.py b/tests/test_one_to_one_with_inheritance.py new file mode 100644 index 000000000..06e1cd8b8 --- /dev/null +++ b/tests/test_one_to_one_with_inheritance.py @@ -0,0 +1,46 @@ +from __future__ import unicode_literals + +from django.db import models +from django.test import TestCase + +from rest_framework import serializers +from tests.models import RESTFrameworkModel + + +# Models +from tests.test_multitable_inheritance import ChildModel + + +# Regression test for #4290 + +class ChildAssociatedModel(RESTFrameworkModel): + child_model = models.OneToOneField(ChildModel) + child_name = models.CharField(max_length=100) + + +# Serializers +class DerivedModelSerializer(serializers.ModelSerializer): + class Meta: + model = ChildModel + fields = ['id', 'name1', 'name2', 'childassociatedmodel'] + + +class ChildAssociatedModelSerializer(serializers.ModelSerializer): + + class Meta: + model = ChildAssociatedModel + fields = ['id', 'child_name'] + + +# Tests +class InheritedModelSerializationTests(TestCase): + + def test_multitable_inherited_model_fields_as_expected(self): + """ + Assert that the parent pointer field is not included in the fields + serialized fields + """ + child = ChildModel(name1='parent name', name2='child name') + serializer = DerivedModelSerializer(child) + self.assertEqual(set(serializer.data.keys()), + set(['name1', 'name2', 'id', 'childassociatedmodel'])) From cca9792ae75f586f76b5de32c0c7e1f489719e8f Mon Sep 17 00:00:00 2001 From: Akshay Sharma Date: Fri, 14 Oct 2016 15:50:09 +0530 Subject: [PATCH 144/175] contributing.md django fix. (#4581) * installation for django added in requirements.txt * added line to install django first in contributing.md * added line to install django first in contributing.md and CONTRIBUTING.md --- CONTRIBUTING.md | 1 + docs/topics/contributing.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 03865b755..415e42ac0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -61,6 +61,7 @@ To run the tests, clone the repository, and then: # Setup the virtual environment virtualenv env source env/bin/activate + pip install django pip install -r requirements.txt # Run the tests diff --git a/docs/topics/contributing.md b/docs/topics/contributing.md index c4ef0efdf..ed7717ae2 100644 --- a/docs/topics/contributing.md +++ b/docs/topics/contributing.md @@ -61,6 +61,7 @@ To run the tests, clone the repository, and then: # Setup the virtual environment virtualenv env source env/bin/activate + pip install django pip install -r requirements.txt # Run the tests From a83997e1edde4d7ea18d8fdd9dec747e5f1a3044 Mon Sep 17 00:00:00 2001 From: Alex Poleha Date: Fri, 14 Oct 2016 13:29:48 +0300 Subject: [PATCH 145/175] Removed incorrect line from generic-views.md (#4583) --- docs/api-guide/generic-views.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md index 5fea8d7e0..c368d0b46 100644 --- a/docs/api-guide/generic-views.md +++ b/docs/api-guide/generic-views.md @@ -220,8 +220,6 @@ Also provides a `.partial_update(request, *args, **kwargs)` method, which is sim If an object is updated this returns a `200 OK` response, with a serialized representation of the object as the body of the response. -If an object is created, for example when making a `DELETE` request followed by a `PUT` request to the same URL, this returns a `201 Created` response, with a serialized representation of the object as the body of the response. - If the request data provided for updating the object was invalid, a `400 Bad Request` response will be returned, with the error details as the body of the response. ## DestroyModelMixin From d5e63d2d7f6570d6306e755bd7366c75dd1b845b Mon Sep 17 00:00:00 2001 From: Fa773N M0nK Date: Tue, 18 Oct 2016 15:36:04 +0530 Subject: [PATCH 146/175] Reflect that '@detail_route' responds to GET only by default. (#4582) --- docs/tutorial/6-viewsets-and-routers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/6-viewsets-and-routers.md b/docs/tutorial/6-viewsets-and-routers.md index 6e1321093..f4eb918bf 100644 --- a/docs/tutorial/6-viewsets-and-routers.md +++ b/docs/tutorial/6-viewsets-and-routers.md @@ -51,7 +51,7 @@ This time we've used the `ModelViewSet` class in order to get the complete set o Notice that we've also used the `@detail_route` decorator to create a custom action, named `highlight`. This decorator can be used to add any custom endpoints that don't fit into the standard `create`/`update`/`delete` style. -Custom actions which use the `@detail_route` decorator will respond to `GET` requests. We can use the `methods` argument if we wanted an action that responded to `POST` requests. +Custom actions which use the `@detail_route` decorator will respond to `GET` requests by default. We can use the `methods` argument if we wanted an action that responded to `POST` requests. The URLs for custom actions by default depend on the method name itself. If you want to change the way url should be constructed, you can include url_path as a decorator keyword argument. From fcff16c5c658fc3033b7ecefdd74221a596ae24c Mon Sep 17 00:00:00 2001 From: Zach Wernberg Date: Tue, 18 Oct 2016 23:14:55 -0500 Subject: [PATCH 147/175] minor typo --- rest_framework/filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 65233978e..c377cec0d 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -222,7 +222,7 @@ class SearchFilter(BaseFilterBackend): # Filtering against a many-to-many field requires us to # call queryset.distinct() in order to avoid duplicate items # in the resulting queryset. - # We try to avoid this is possible, for performance reasons. + # We try to avoid this if possible, for performance reasons. queryset = distinct(queryset, base) return queryset From 3f6004c5a9edab6336e93da85ce3849dee0b1311 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 20 Oct 2016 09:42:40 +0100 Subject: [PATCH 148/175] Use pk for URL conf and views. (#4592) --- docs/tutorial/1-serialization.md | 6 +++--- docs/tutorial/2-requests-and-responses.md | 8 ++++---- docs/tutorial/3-class-based-views.md | 18 +++++++++--------- .../4-authentication-and-permissions.md | 2 +- .../5-relationships-and-hyperlinked-apis.md | 8 ++++---- docs/tutorial/6-viewsets-and-routers.md | 6 +++--- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index 434072e11..04fb6914a 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -259,12 +259,12 @@ Note that because we want to be able to POST to this view from clients that won' We'll also need a view which corresponds to an individual snippet, and can be used to retrieve, update or delete the snippet. @csrf_exempt - def snippet_detail(request, id): + def snippet_detail(request, pk): """ Retrieve, update or delete a code snippet. """ try: - snippet = Snippet.objects.get(id=id) + snippet = Snippet.objects.get(pk=pk) except Snippet.DoesNotExist: return HttpResponse(status=404) @@ -291,7 +291,7 @@ Finally we need to wire these views up. Create the `snippets/urls.py` file: urlpatterns = [ url(r'^snippets/$', views.snippet_list), - url(r'^snippets/(?P[0-9]+)/$', views.snippet_detail), + url(r'^snippets/(?P[0-9]+)/$', views.snippet_detail), ] We also need to wire up the root urlconf, in the `tutorial/urls.py` file, to include our snippet app's URLs. diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md index 4aa0062a3..5c020a1f7 100644 --- a/docs/tutorial/2-requests-and-responses.md +++ b/docs/tutorial/2-requests-and-responses.md @@ -66,12 +66,12 @@ Our instance view is an improvement over the previous example. It's a little mo Here is the view for an individual snippet, in the `views.py` module. @api_view(['GET', 'PUT', 'DELETE']) - def snippet_detail(request, id): + def snippet_detail(request, pk): """ Retrieve, update or delete a snippet instance. """ try: - snippet = Snippet.objects.get(id=id) + snippet = Snippet.objects.get(pk=pk) except Snippet.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) @@ -104,7 +104,7 @@ Start by adding a `format` keyword argument to both of the views, like so. and - def snippet_detail(request, id, format=None): + def snippet_detail(request, pk, format=None): Now update the `urls.py` file slightly, to append a set of `format_suffix_patterns` in addition to the existing URLs. @@ -114,7 +114,7 @@ Now update the `urls.py` file slightly, to append a set of `format_suffix_patter urlpatterns = [ url(r'^snippets/$', views.snippet_list), - url(r'^snippets/(?P[0-9]+)$', views.snippet_detail), + url(r'^snippets/(?P[0-9]+)$', views.snippet_detail), ] urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/docs/tutorial/3-class-based-views.md b/docs/tutorial/3-class-based-views.md index 6303994cd..f018666f5 100644 --- a/docs/tutorial/3-class-based-views.md +++ b/docs/tutorial/3-class-based-views.md @@ -36,27 +36,27 @@ So far, so good. It looks pretty similar to the previous case, but we've got be """ Retrieve, update or delete a snippet instance. """ - def get_object(self, id): + def get_object(self, pk): try: - return Snippet.objects.get(id=id) + return Snippet.objects.get(pk=pk) except Snippet.DoesNotExist: raise Http404 - def get(self, request, id, format=None): - snippet = self.get_object(id) + def get(self, request, pk, format=None): + snippet = self.get_object(pk) serializer = SnippetSerializer(snippet) return Response(serializer.data) - def put(self, request, id, format=None): - snippet = self.get_object(id) + def put(self, request, pk, format=None): + snippet = self.get_object(pk) serializer = SnippetSerializer(snippet, data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - def delete(self, request, id, format=None): - snippet = self.get_object(id) + def delete(self, request, pk, format=None): + snippet = self.get_object(pk) snippet.delete() return Response(status=status.HTTP_204_NO_CONTENT) @@ -70,7 +70,7 @@ We'll also need to refactor our `urls.py` slightly now we're using class-based v urlpatterns = [ url(r'^snippets/$', views.SnippetList.as_view()), - url(r'^snippets/(?P[0-9]+)/$', views.SnippetDetail.as_view()), + url(r'^snippets/(?P[0-9]+)/$', views.SnippetDetail.as_view()), ] urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/docs/tutorial/4-authentication-and-permissions.md b/docs/tutorial/4-authentication-and-permissions.md index 958f9d3f0..098194c29 100644 --- a/docs/tutorial/4-authentication-and-permissions.md +++ b/docs/tutorial/4-authentication-and-permissions.md @@ -88,7 +88,7 @@ Make sure to also import the `UserSerializer` class Finally we need to add those views into the API, by referencing them from the URL conf. Add the following to the patterns in `urls.py`. url(r'^users/$', views.UserList.as_view()), - url(r'^users/(?P[0-9]+)/$', views.UserDetail.as_view()), + url(r'^users/(?P[0-9]+)/$', views.UserDetail.as_view()), ## Associating Snippets with Users diff --git a/docs/tutorial/5-relationships-and-hyperlinked-apis.md b/docs/tutorial/5-relationships-and-hyperlinked-apis.md index 9fb6c53e0..9fd61b414 100644 --- a/docs/tutorial/5-relationships-and-hyperlinked-apis.md +++ b/docs/tutorial/5-relationships-and-hyperlinked-apis.md @@ -48,7 +48,7 @@ We'll add a url pattern for our new API root in `snippets/urls.py`: And then add a url pattern for the snippet highlights: - url(r'^snippets/(?P[0-9]+)/highlight/$', views.SnippetHighlight.as_view()), + url(r'^snippets/(?P[0-9]+)/highlight/$', views.SnippetHighlight.as_view()), ## Hyperlinking our API @@ -116,16 +116,16 @@ After adding all those names into our URLconf, our final `snippets/urls.py` file url(r'^snippets/$', views.SnippetList.as_view(), name='snippet-list'), - url(r'^snippets/(?P[0-9]+)/$', + url(r'^snippets/(?P[0-9]+)/$', views.SnippetDetail.as_view(), name='snippet-detail'), - url(r'^snippets/(?P[0-9]+)/highlight/$', + url(r'^snippets/(?P[0-9]+)/highlight/$', views.SnippetHighlight.as_view(), name='snippet-highlight'), url(r'^users/$', views.UserList.as_view(), name='user-list'), - url(r'^users/(?P[0-9]+)/$', + url(r'^users/(?P[0-9]+)/$', views.UserDetail.as_view(), name='user-detail') ]) diff --git a/docs/tutorial/6-viewsets-and-routers.md b/docs/tutorial/6-viewsets-and-routers.md index f4eb918bf..6189c7771 100644 --- a/docs/tutorial/6-viewsets-and-routers.md +++ b/docs/tutorial/6-viewsets-and-routers.md @@ -92,10 +92,10 @@ Now that we've bound our resources into concrete views, we can register the view urlpatterns = format_suffix_patterns([ url(r'^$', api_root), url(r'^snippets/$', snippet_list, name='snippet-list'), - url(r'^snippets/(?P[0-9]+)/$', snippet_detail, name='snippet-detail'), - url(r'^snippets/(?P[0-9]+)/highlight/$', snippet_highlight, name='snippet-highlight'), + url(r'^snippets/(?P[0-9]+)/$', snippet_detail, name='snippet-detail'), + url(r'^snippets/(?P[0-9]+)/highlight/$', snippet_highlight, name='snippet-highlight'), url(r'^users/$', user_list, name='user-list'), - url(r'^users/(?P[0-9]+)/$', user_detail, name='user-detail') + url(r'^users/(?P[0-9]+)/$', user_detail, name='user-detail') ]) ## Using Routers From 2395fb53867538ad83db335f3aaaef472eb2f0f4 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 20 Oct 2016 10:47:09 +0100 Subject: [PATCH 149/175] Deprecate DjangoFilter backend (#4593) Deprecate the built-in `rest_framework.filters.DjangoFilterBackend` in favour of the third-party `django_filters.rest_framework.DjangoFilterBackend`. --- docs/api-guide/filtering.md | 40 +++++--- requirements/requirements-optionals.txt | 2 +- rest_framework/filters.py | 128 ++++-------------------- tests/test_filters.py | 34 +++++++ 4 files changed, 83 insertions(+), 121 deletions(-) diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md index 40a097174..1b49d3a73 100644 --- a/docs/api-guide/filtering.md +++ b/docs/api-guide/filtering.md @@ -89,24 +89,24 @@ Generic filters can also present themselves as HTML controls in the browsable AP ## Setting filter backends -The default filter backends may be set globally, using the `DEFAULT_FILTER_BACKENDS` setting. For example. +The default filter backends may be set globally, using the `DEFAULT_FILTER_BACKENDS` setting. For example. REST_FRAMEWORK = { - 'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',) + 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',) } You can also set the filter backends on a per-view, or per-viewset basis, using the `GenericAPIView` class-based views. + import django_filters from django.contrib.auth.models import User from myapp.serializers import UserSerializer - from rest_framework import filters from rest_framework import generics class UserListView(generics.ListAPIView): queryset = User.objects.all() serializer_class = UserSerializer - filter_backends = (filters.DjangoFilterBackend,) + filter_backends = (django_filters.rest_framework.DjangoFilterBackend,) ## Filtering and object lookups @@ -139,12 +139,27 @@ Note that you can use both an overridden `.get_queryset()` and generic filtering ## DjangoFilterBackend -The `DjangoFilterBackend` class supports highly customizable field filtering, using the [django-filter package][django-filter]. +The `django-filter` library includes a `DjangoFilterBackend` class which +supports highly customizable field filtering for REST framework. -To use REST framework's `DjangoFilterBackend`, first install `django-filter`. +To use `DjangoFilterBackend`, first install `django-filter`. pip install django-filter +You should now either add the filter backend to your settings: + + REST_FRAMEWORK = { + 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',) + } + +Or add the filter backend to an individual View or ViewSet. + + from django_filters.rest_framework import DjangoFilterBackend + + class UserListView(generics.ListAPIView): + ... + filter_backends = (DjangoFilterBackend,) + If you are using the browsable API or admin API you may also want to install `django-crispy-forms`, which will enhance the presentation of the filter forms in HTML views, by allowing them to render Bootstrap 3 HTML. pip install django-crispy-forms @@ -174,10 +189,9 @@ For more advanced filtering requirements you can specify a `FilterSet` class tha import django_filters from myapp.models import Product from myapp.serializers import ProductSerializer - from rest_framework import filters from rest_framework import generics - class ProductFilter(filters.FilterSet): + class ProductFilter(django_filters.rest_framework.FilterSet): min_price = django_filters.NumberFilter(name="price", lookup_expr='gte') max_price = django_filters.NumberFilter(name="price", lookup_expr='lte') class Meta: @@ -187,7 +201,7 @@ For more advanced filtering requirements you can specify a `FilterSet` class tha class ProductList(generics.ListAPIView): queryset = Product.objects.all() serializer_class = ProductSerializer - filter_backends = (filters.DjangoFilterBackend,) + filter_backends = (django_filters.rest_framework.DjangoFilterBackend,) filter_class = ProductFilter @@ -199,12 +213,12 @@ You can also span relationships using `django-filter`, let's assume that each product has foreign key to `Manufacturer` model, so we create filter that filters using `Manufacturer` name. For example: + import django_filters from myapp.models import Product from myapp.serializers import ProductSerializer - from rest_framework import filters from rest_framework import generics - class ProductFilter(filters.FilterSet): + class ProductFilter(django_filters.rest_framework.FilterSet): class Meta: model = Product fields = ['category', 'in_stock', 'manufacturer__name'] @@ -218,10 +232,9 @@ This is nice, but it exposes the Django's double underscore convention as part o import django_filters from myapp.models import Product from myapp.serializers import ProductSerializer - from rest_framework import filters from rest_framework import generics - class ProductFilter(filters.FilterSet): + class ProductFilter(django_filters.rest_framework.FilterSet): manufacturer = django_filters.CharFilter(name="manufacturer__name") class Meta: @@ -454,4 +467,3 @@ The [djangorestframework-word-filter][django-rest-framework-word-search-filter] [django-rest-framework-word-search-filter]: https://github.com/trollknurr/django-rest-framework-word-search-filter [django-url-filter]: https://github.com/miki725/django-url-filter [drf-url-filter]: https://github.com/manjitkumar/drf-url-filters - diff --git a/requirements/requirements-optionals.txt b/requirements/requirements-optionals.txt index 31f24f4b7..86c4f7709 100644 --- a/requirements/requirements-optionals.txt +++ b/requirements/requirements-optionals.txt @@ -1,5 +1,5 @@ # Optional packages which may be used with REST framework. markdown==2.6.4 django-guardian==1.4.6 -django-filter==0.14.0 +django-filter==0.15.3 coreapi==2.0.8 diff --git a/rest_framework/filters.py b/rest_framework/filters.py index c377cec0d..f55297b39 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -5,9 +5,9 @@ returned by list views. from __future__ import unicode_literals import operator +import warnings from functools import reduce -from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.db import models from django.db.models.constants import LOOKUP_SEP @@ -16,50 +16,10 @@ from django.utils import six from django.utils.translation import ugettext_lazy as _ from rest_framework.compat import ( - coreapi, crispy_forms, distinct, django_filters, guardian, template_render + coreapi, distinct, django_filters, guardian, template_render ) from rest_framework.settings import api_settings -if 'crispy_forms' in settings.INSTALLED_APPS and crispy_forms and django_filters: - # If django-crispy-forms is installed, use it to get a bootstrap3 rendering - # of the DjangoFilterBackend controls when displayed as HTML. - from crispy_forms.helper import FormHelper - from crispy_forms.layout import Layout, Submit - - class FilterSet(django_filters.FilterSet): - def __init__(self, *args, **kwargs): - super(FilterSet, self).__init__(*args, **kwargs) - for field in self.form.fields.values(): - field.help_text = None - - layout_components = list(self.form.fields.keys()) + [ - Submit('', _('Submit'), css_class='btn-default'), - ] - - helper = FormHelper() - helper.form_method = 'GET' - helper.template_pack = 'bootstrap3' - helper.layout = Layout(*layout_components) - - self.form.helper = helper - - filter_template = 'rest_framework/filters/django_filter_crispyforms.html' - -elif django_filters: - # If django-crispy-forms is not installed, use the standard - # 'form.as_p' rendering when DjangoFilterBackend is displayed as HTML. - class FilterSet(django_filters.FilterSet): - def __init__(self, *args, **kwargs): - super(FilterSet, self).__init__(*args, **kwargs) - for field in self.form.fields.values(): - field.help_text = None - - filter_template = 'rest_framework/filters/django_filter.html' - -else: - FilterSet = None - filter_template = None - class BaseFilterBackend(object): """ @@ -77,78 +37,34 @@ class BaseFilterBackend(object): return [] +class FilterSet(object): + def __new__(cls, *args, **kwargs): + warnings.warn( + "The built in 'rest_framework.filters.FilterSet' is pending deprecation. " + "You should use 'django_filters.rest_framework.FilterSet' instead.", + PendingDeprecationWarning + ) + from django_filters.rest_framework import FilterSet + return FilterSet(*args, **kwargs) + + class DjangoFilterBackend(BaseFilterBackend): """ A filter backend that uses django-filter. """ - default_filter_set = FilterSet - template = filter_template - - def __init__(self): + def __new__(cls, *args, **kwargs): assert django_filters, 'Using DjangoFilterBackend, but django-filter is not installed' + assert django_filters.VERSION >= (0, 15, 3), 'django-filter 0.15.3 and above is required' - def get_filter_class(self, view, queryset=None): - """ - Return the django-filters `FilterSet` used to filter the queryset. - """ - filter_class = getattr(view, 'filter_class', None) - filter_fields = getattr(view, 'filter_fields', None) + warnings.warn( + "The built in 'rest_framework.filters.DjangoFilterBackend' is pending deprecation. " + "You should use 'django_filters.rest_framework.DjangoFilterBackend' instead.", + PendingDeprecationWarning + ) - if filter_class: - filter_model = filter_class.Meta.model + from django_filters.rest_framework import DjangoFilterBackend - assert issubclass(queryset.model, filter_model), \ - 'FilterSet model %s does not match queryset model %s' % \ - (filter_model, queryset.model) - - return filter_class - - if filter_fields: - class AutoFilterSet(self.default_filter_set): - class Meta: - model = queryset.model - fields = filter_fields - - return AutoFilterSet - - return None - - def filter_queryset(self, request, queryset, view): - filter_class = self.get_filter_class(view, queryset) - - if filter_class: - return filter_class(request.query_params, queryset=queryset).qs - - return queryset - - def to_html(self, request, queryset, view): - filter_class = self.get_filter_class(view, queryset) - if not filter_class: - return None - filter_instance = filter_class(request.query_params, queryset=queryset) - context = { - 'filter': filter_instance - } - template = loader.get_template(self.template) - return template_render(template, context) - - def get_schema_fields(self, view): - assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' - filter_class = getattr(view, 'filter_class', None) - if filter_class: - return [ - coreapi.Field(name=field_name, required=False, location='query') - for field_name in filter_class().filters.keys() - ] - - filter_fields = getattr(view, 'filter_fields', None) - if filter_fields: - return [ - coreapi.Field(name=field_name, required=False, location='query') - for field_name in filter_fields - ] - - return [] + return DjangoFilterBackend(*args, **kwargs) class SearchFilter(BaseFilterBackend): diff --git a/tests/test_filters.py b/tests/test_filters.py index c67412dd7..9795230d6 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals import datetime import unittest +import warnings from decimal import Decimal from django.conf.urls import url @@ -134,6 +135,39 @@ class IntegrationTestFiltering(CommonFilteringTestCase): Integration tests for filtered list views. """ + @unittest.skipUnless(django_filters, 'django-filter not installed') + def test_backend_deprecation(self): + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + + view = FilterFieldsRootView.as_view() + request = factory.get('/') + response = view(request).render() + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, self.data) + + self.assertTrue(issubclass(w[-1].category, PendingDeprecationWarning)) + self.assertIn("'rest_framework.filters.DjangoFilterBackend' is pending deprecation.", str(w[-1].message)) + + @unittest.skipUnless(django_filters, 'django-filter not installed') + def test_no_df_deprecation(self): + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + + import django_filters.rest_framework + + class DFFilterFieldsRootView(FilterFieldsRootView): + filter_backends = (django_filters.rest_framework.DjangoFilterBackend,) + + view = DFFilterFieldsRootView.as_view() + request = factory.get('/') + response = view(request).render() + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, self.data) + self.assertEqual(len(w), 0) + @unittest.skipUnless(django_filters, 'django-filter not installed') def test_get_filtered_fields_root_view(self): """ From 3b9afb571b6e7c2a445be675163c9f43d680d899 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 20 Oct 2016 16:25:40 +0100 Subject: [PATCH 150/175] Version 3.5.0 (#4596) --- docs/api-guide/schemas.md | 47 +++++- docs/api-guide/testing.md | 4 +- docs/img/raml.png | Bin 0 -> 37216 bytes docs/index.md | 2 + docs/topics/3.5-announcement.md | 266 ++++++++++++++++++++++++++++++++ docs/topics/api-clients.md | 2 +- docs/topics/release-notes.md | 9 ++ mkdocs.yml | 1 + 8 files changed, 324 insertions(+), 7 deletions(-) create mode 100644 docs/img/raml.png create mode 100644 docs/topics/3.5-announcement.md diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md index 16d2bbb01..7da619034 100644 --- a/docs/api-guide/schemas.md +++ b/docs/api-guide/schemas.md @@ -344,12 +344,12 @@ Typically you'll instantiate `SchemaGenerator` with a single argument, like so: Arguments: -* `title` - The name of the API. **required** +* `title` **required** - The name of the API. * `url` - The root URL of the API schema. This option is not required unless the schema is included under path prefix. * `patterns` - A list of URLs to inspect when generating the schema. Defaults to the project's URL conf. * `urlconf` - A URL conf module name to use when generating the schema. Defaults to `settings.ROOT_URLCONF`. -### get_schema() +### get_schema(self, request) Returns a `coreapi.Document` instance that represents the API schema. @@ -359,9 +359,48 @@ Returns a `coreapi.Document` instance that represents the API schema. generator = schemas.SchemaGenerator(title='Bookings API') return Response(generator.get_schema()) -Arguments: +The `request` argument is optional, and may be used if you want to apply per-user +permissions to the resulting schema generation. -* `request` - The incoming request. Optionally used if you want to apply per-user permissions to the schema-generation. +### get_links(self, request) + +Return a nested dictionary containing all the links that should be included in the API schema. + +This is a good point to override if you want to modify the resulting structure of the generated schema, +as you can build a new dictionary with a different layout. + +### get_link(self, path, method, view) + +Returns a `coreapi.Link` instance corresponding to the given view. + +You can override this if you need to provide custom behaviors for particular views. + +### get_description(self, path, method, view) + +Returns a string to use as the link description. By default this is based on the +view docstring as described in the "Schemas as Documentation" section above. + +### get_encoding(self, path, method, view) + +Returns a string to indicate the encoding for any request body, when interacting +with the given view. Eg. `'application/json'`. May return a blank string for views +that do not expect a request body. + +### get_path_fields(self, path, method, view): + +Return a list of `coreapi.Link()` instances. One for each path parameter in the URL. + +### get_serializer_fields(self, path, method, view) + +Return a list of `coreapi.Link()` instances. One for each field in the serializer class used by the view. + +### get_pagination_fields(self, path, method, view + +Return a list of `coreapi.Link()` instances, as returned by the `get_schema_fields()` method on any pagination class used by the view. + +### get_filter_fields(self, path, method, view) + +Return a list of `coreapi.Link()` instances, as returned by the `get_schema_fields()` method of any filter classes used by the view. --- diff --git a/docs/api-guide/testing.md b/docs/api-guide/testing.md index fdd60b7f4..1f8c89233 100644 --- a/docs/api-guide/testing.md +++ b/docs/api-guide/testing.md @@ -194,6 +194,7 @@ directly. client = RequestsClient() response = client.get('http://testserver/users/') + assert response.status_code == 200 Note that the requests client requires you to pass fully qualified URLs. @@ -251,9 +252,8 @@ The CoreAPIClient allows you to interact with your API using the Python `coreapi` client library. # Fetch the API schema - url = reverse('schema') client = CoreAPIClient() - schema = client.get(url) + schema = client.get('http://testserver/schema/') # Create a new organisation params = {'name': 'MegaCorp', 'status': 'active'} diff --git a/docs/img/raml.png b/docs/img/raml.png new file mode 100644 index 0000000000000000000000000000000000000000..87790dc484f9c6c0617189ae2766bdc1e00a3bfa GIT binary patch literal 37216 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 literal 0 HcmV?d00001 diff --git a/docs/index.md b/docs/index.md index 3019467ed..9b0913f00 100644 --- a/docs/index.md +++ b/docs/index.md @@ -244,6 +244,7 @@ General guides to using REST framework. * [3.2 Announcement][3.2-announcement] * [3.3 Announcement][3.3-announcement] * [3.4 Announcement][3.4-announcement] +* [3.5 Announcement][3.5-announcement] * [Kickstarter Announcement][kickstarter-announcement] * [Mozilla Grant][mozilla-grant] * [Funding][funding] @@ -370,6 +371,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [3.2-announcement]: topics/3.2-announcement.md [3.3-announcement]: topics/3.3-announcement.md [3.4-announcement]: topics/3.4-announcement.md +[3.5-announcement]: topics/3.5-announcement.md [kickstarter-announcement]: topics/kickstarter-announcement.md [mozilla-grant]: topics/mozilla-grant.md [funding]: topics/funding.md diff --git a/docs/topics/3.5-announcement.md b/docs/topics/3.5-announcement.md new file mode 100644 index 000000000..2ed8adf8e --- /dev/null +++ b/docs/topics/3.5-announcement.md @@ -0,0 +1,266 @@ + + +# Django REST framework 3.5 + +The 3.5 release is the second in a planned series that is addressing schema +generation, hypermedia support, API client libraries, and finally realtime support. + +--- + +## Funding + +The 3.5 release would not have been possible without our [collaborative funding model][funding]. +If you use REST framework commercially and would like to see this work continue, +we strongly encourage you to invest in its continued development by +**[signing up for a paid plan][funding]**. + + +
+ +*Many thanks to all our [sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), and [Machinalis](http://www.machinalis.com/#services).* + +--- + +## Improved schema generation + +Docstrings on views are now pulled through into schema definitions, allowing +you to [use the schema definition to document your API][schema-docs]. + +There is now also a shortcut function, `get_schema_view()`, which makes it easier to +[adding schema views][schema-view] to your API. + +For example, to include a swagger schema to your API, you would do the following: + +* Run `pip install django-rest-swagger`. + +* Add `'rest_framework_swagger'` to your `INSTALLED_APPS` setting. + +* Include the schema view in your URL conf: + +```py +from rest_framework.schemas import get_schema_view +from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer + +schema_view = get_schema_view( + title='Example API', + renderer_classes=[OpenAPIRenderer, SwaggerUIRenderer] +) + +urlpatterns = [ + url(r'^swagger/$', schema_view), + ... +] +``` + +There have been a large number of fixes to the schema generation. These should +resolve issues for anyone using the latest version of the `django-rest-swagger` +package. + +Some of these changes do affect the resulting schema structure, +so if you're already using schema generation you should make sure to review +[the deprecation notes](#deprecations), particularly if you're currently using +a dynamic client library to interact with your API. + +Finally, we're also now exposing the schema generation as a +[publicly documented API][schema-generation-api], allowing you to more easily +override the behaviour. + +## Requests test client + +You can now test your project using the `requests` library. + +This exposes exactly the same interface as if you were using a standard +requests session instance. + + client = RequestsClient() + response = client.get('http://testserver/users/') + assert response.status_code == 200 + +Rather than sending any HTTP requests to the network, this interface will +coerce all outgoing requests into WSGI, and call into your application directly. + +## Core API client + +You can also now test your project by interacting with it using the `coreapi` +client library. + + # Fetch the API schema + client = CoreAPIClient() + schema = client.get('http://testserver/schema/') + + # Create a new organisation + params = {'name': 'MegaCorp', 'status': 'active'} + client.action(schema, ['organisations', 'create'], params) + + # Ensure that the organisation exists in the listing + data = client.action(schema, ['organisations', 'list']) + assert(len(data) == 1) + assert(data == [{'name': 'MegaCorp', 'status': 'active'}]) + +Again, this will call directly into the application using the WSGI interface, +rather than making actual network calls. + +This is a good option if you are planning for clients to mainly interact with +your API using the `coreapi` client library, or some other auto-generated client. + +## Live tests + +One interesting aspect of both the `requests` client and the `coreapi` client +is that they allow you to write tests in such a way that they can also be made +to run against a live service. + +By switching the WSGI based client instances to actual instances of `requests.Session` +or `coreapi.Client` you can have the test cases make actual network calls. + +Being able to write test cases that can exercise your staging or production +environment is a powerful tool. However in order to do this, you'll need to pay +close attention to how you handle setup and teardown to ensure a strict isolation +of test data from other live or staging data. + +## RAML support + +We now have preliminary support for [RAML documentation generation][django-rest-raml]. + +![RAML Example][raml-image] + +Further work on the encoding and documentation generation is planned, in order to +make features such as the 'Try it now' support available at a later date. + +This work also now means that you can use the Core API client libraries to interact +with APIs that expose a RAML specification. The [RAML codec][raml-codec] gives some examples of +interacting with the Spotify API in this way. + +## Validation codes + +Exceptions raised by REST framework now include short code identifiers. +When used together with our customizable error handling, this now allows you to +modify the style of API error messages. + +As an example, this allows for the following style of error responses: + + { + "message": "You do not have permission to perform this action.", + "code": "permission_denied" + } + +This is particularly useful with validation errors, which use appropriate +codes to identify differing kinds of failure... + + { + "name": {"message": "This field is required.", "code": "required"}, + "age": {"message": "A valid integer is required.", "code": "invalid"} + } + +## Client upload & download support + +The Python `coreapi` client library and the Core API command line tool both +now fully support file [uploads][uploads] and [downloads][downloads]. + +--- + +## Deprecations + +### Generating schemas from Router + +The router arguments for generating a schema view, such as `schema_title`, +are now pending deprecation. + +Instead of using `DefaultRouter(schema_title='Example API')`, you should use +the `get_schema_view()` function, and include the view in your URL conf. + +Make sure to include the view before your router urls. For example: + + from rest_framework.schemas import get_schema_view + from my_project.routers import router + + schema_view = get_schema_view(title='Example API') + + urlpatterns = [ + url('^$', schema_view), + url(r'^', include(router.urls)), + ] + +### Schema path representations + +The `'pk'` identifier in schema paths is now mapped onto the actually model field +name by default. This will typically be `'id'`. + +This gives a better external representation for schemas, with less implementation +detail being exposed. It also reflects the behaviour of using a ModelSerializer +class with `fields = '__all__'`. + +You can revert to the previous behaviour by setting `'SCHEMA_COERCE_PATH_PK': False` +in the REST framework settings. + +### Schema action name representations + +The internal `retrieve()` and `destroy()` method names are now coerced to an +external representation of `read` and `delete`. + +You can revert to the previous behaviour by setting `'SCHEMA_COERCE_METHOD_NAMES': {}` +in the REST framework settings. + +### DjangoFilterBackend + +The functionality of the built-in `DjangoFilterBackend` is now completely +included by the `django-filter` package. + +You should change your imports and REST framework filter settings as follows: + +* `rest_framework.filters.DjangoFilterBackend` becomes `django_filters.rest_framework.DjangoFilterBackend`. +* `rest_framework.filters.FilterSet` becomes `django_filters.rest_framework.FilterSet`. + +The existing imports will continue to work but are now pending deprecation. + +### CoreJSON media type + +The media type for `CoreJSON` is now `application/json+coreapi`, rather than +the previous `application/vnd.json+coreapi`. This brings it more into line with +other custom media types, such as those used by Swagger and RAML. + +The clients currently accept either media type. The old style-media type will +be deprecated at a later date. + +### ModelSerializer 'fields' and 'exclude' + +ModelSerializer and HyperlinkedModelSerializer must include either a fields +option, or an exclude option. The fields = '__all__' shortcut may be used to +explicitly include all fields. + +Failing to set either `fields` or `exclude` raised a pending deprecation warning +in version 3.3 and raised a deprecation warning in 3.4. Its usage is now mandatory. + +--- + +[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors +[funding]: funding.md +[uploads]: http://core-api.github.io/python-client/api-guide/utils/#file +[downloads]: http://core-api.github.io/python-client/api-guide/codecs/#downloadcodec +[schema-generation-api]: ../api-guide/schemas/#schemagenerator +[schema-docs]: ../api-guide/schemas/#schemas-as-documentation +[schema-view]: ../api-guide/schemas/#the-get_schema_view-shortcut +[django-rest-raml]: https://github.com/tomchristie/django-rest-raml +[raml-image]: ../img/raml.png +[raml-codec]: https://github.com/core-api/python-raml-codec diff --git a/docs/topics/api-clients.md b/docs/topics/api-clients.md index f17f5e4d4..c12551aa6 100644 --- a/docs/topics/api-clients.md +++ b/docs/topics/api-clients.md @@ -257,7 +257,7 @@ Codecs are responsible for encoding or decoding Documents. The decoding process is used by a client to take a bytestring of an API schema definition, and returning the Core API `Document` that represents that interface. -A codec should be associated with a particular media type, such as **TODO**. +A codec should be associated with a particular media type, such as `'application/coreapi+json'`. This media type is used by the server in the response `Content-Type` header, in order to indicate what kind of data is being returned in the response. diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 446abdd14..30244f6d2 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -38,6 +38,14 @@ You can determine your currently installed version using `pip freeze`: --- +## 3.5.x series + +### 3.5.0 + +**Date**: [20th October 2016][3.5.0-milestone] + +--- + ## 3.4.x series ### 3.4.7 @@ -596,6 +604,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.4.5-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.5+Release%22 [3.4.6-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.6+Release%22 [3.4.7-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.7+Release%22 +[3.5.0-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.5.0+Release%22 [gh2013]: https://github.com/tomchristie/django-rest-framework/issues/2013 diff --git a/mkdocs.yml b/mkdocs.yml index 0b89988b1..01c59caaa 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -66,6 +66,7 @@ pages: - '3.2 Announcement': 'topics/3.2-announcement.md' - '3.3 Announcement': 'topics/3.3-announcement.md' - '3.4 Announcement': 'topics/3.4-announcement.md' + - '3.5 Announcement': 'topics/3.5-announcement.md' - 'Kickstarter Announcement': 'topics/kickstarter-announcement.md' - 'Mozilla Grant': 'topics/mozilla-grant.md' - 'Funding': 'topics/funding.md' From c6f1686571026d1f2f3bb6bf9d73ca9a61b93182 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 20 Oct 2016 16:26:56 +0100 Subject: [PATCH 151/175] Remove erronous file [ci skip] --- schema-support | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 schema-support diff --git a/schema-support b/schema-support deleted file mode 100644 index e69de29bb..000000000 From 1aa6dff0b54e39d1fe7a0c7058f6f74ee01475fd Mon Sep 17 00:00:00 2001 From: Maxime Lorant Date: Thu, 20 Oct 2016 17:47:59 +0200 Subject: [PATCH 152/175] Fix code formatting missing in 3.5 announcement (#4597) ... in section ModelSerializer 'fields' and 'exclude' --- docs/topics/3.5-announcement.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/3.5-announcement.md b/docs/topics/3.5-announcement.md index 2ed8adf8e..ea50b2418 100644 --- a/docs/topics/3.5-announcement.md +++ b/docs/topics/3.5-announcement.md @@ -246,7 +246,7 @@ be deprecated at a later date. ### ModelSerializer 'fields' and 'exclude' ModelSerializer and HyperlinkedModelSerializer must include either a fields -option, or an exclude option. The fields = '__all__' shortcut may be used to +option, or an exclude option. The `fields = '__all__'` shortcut may be used to explicitly include all fields. Failing to set either `fields` or `exclude` raised a pending deprecation warning From 856f086ce35e1b8e2abda16c860371543a8b12f2 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 21 Oct 2016 14:42:42 +0100 Subject: [PATCH 153/175] Remove broken wheel check in setup.py [ci skip] --- setup.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/setup.py b/setup.py index 86870489f..ca62366ed 100755 --- a/setup.py +++ b/setup.py @@ -61,9 +61,6 @@ if sys.argv[-1] == 'publish': import pypandoc except ImportError: print("pypandoc not installed.\nUse `pip install pypandoc`.\nExiting.") - if os.system("pip freeze | grep wheel"): - print("wheel not installed.\nUse `pip install wheel`.\nExiting.") - sys.exit() if os.system("pip freeze | grep twine"): print("twine not installed.\nUse `pip install twine`.\nExiting.") sys.exit() From e3686aca93a8224280b38b7669342bcadb24176e Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 21 Oct 2016 14:47:26 +0100 Subject: [PATCH 154/175] Don't use bare 'raise'. [ci skip] --- rest_framework/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/views.py b/rest_framework/views.py index a8710c7a0..07575fba7 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -445,7 +445,7 @@ class APIView(View): renderer_format = getattr(request.accepted_renderer, 'format') use_plaintext_traceback = renderer_format not in ('html', 'api', 'admin') request.force_plaintext_errors(use_plaintext_traceback) - raise + raise exc # Note: Views are made CSRF exempt from within `as_view` as to prevent # accidental removal of this exemption in cases where `dispatch` needs to From 0b346e94b1ed3016e79dd39be3ee48691cecb035 Mon Sep 17 00:00:00 2001 From: Lukasz Karolewski Date: Fri, 21 Oct 2016 07:00:25 -0700 Subject: [PATCH 155/175] changing order of imports (#4601) when using with django-filter and rest_framework_swagger need to import coreapi before django-filter as django filter tries to load rest_framework.coreapi which is undefined at this point --- rest_framework/compat.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 7ec39ba63..2d6e7843c 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -170,6 +170,16 @@ except ImportError: JSONField = None +# coreapi is optional (Note that uritemplate is a dependency of coreapi) +try: + import coreapi + import uritemplate +except (ImportError, SyntaxError): + # SyntaxError is possible under python 3.2 + coreapi = None + uritemplate = None + + # django-filter is optional try: import django_filters @@ -184,16 +194,6 @@ except ImportError: crispy_forms = None -# coreapi is optional (Note that uritemplate is a dependency of coreapi) -try: - import coreapi - import uritemplate -except (ImportError, SyntaxError): - # SyntaxError is possible under python 3.2 - coreapi = None - uritemplate = None - - # requests is optional try: import requests From f1bdce17b547859a6d59e5c0fe85b9c46d061f44 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 21 Oct 2016 15:21:23 +0100 Subject: [PATCH 156/175] Fix for case of ListSerializer with single item (#4609) --- rest_framework/serializers.py | 4 ++-- tests/test_serializer.py | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 098c3cd23..39987cd07 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -507,7 +507,7 @@ class Serializer(BaseSerializer): @property def errors(self): ret = super(Serializer, self).errors - if isinstance(ret, list) and len(ret) == 1 and ret[0].code == 'null': + if isinstance(ret, list) and len(ret) == 1 and getattr(ret[0], 'code', None) == 'null': # Edge case. Provide a more descriptive error than # "this field may not be null", when no data is passed. detail = ErrorDetail('No data provided', code='null') @@ -705,7 +705,7 @@ class ListSerializer(BaseSerializer): @property def errors(self): ret = super(ListSerializer, self).errors - if isinstance(ret, list) and len(ret) == 1 and ret[0].code == 'null': + if isinstance(ret, list) and len(ret) == 1 and getattr(ret[0], 'code', None) == 'null': # Edge case. Provide a more descriptive error than # "this field may not be null", when no data is passed. detail = ErrorDetail('No data provided', code='null') diff --git a/tests/test_serializer.py b/tests/test_serializer.py index a2817f6a4..32be39faa 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -357,3 +357,16 @@ class TestSerializerValidationWithCompiledRegexField: assert serializer.is_valid() assert serializer.validated_data == {'name': '2'} assert serializer.errors == {} + + +class Test4606Regression: + def setup(self): + class ExampleSerializer(serializers.Serializer): + name = serializers.CharField(required=True) + choices = serializers.CharField(required=True) + self.Serializer = ExampleSerializer + + def test_4606_regression(self): + serializer = self.Serializer(data=[{"name": "liz"}], many=True) + with pytest.raises(serializers.ValidationError): + serializer.is_valid(raise_exception=True) From d647d37a99df3673000f17f7d565a8dab0770805 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 21 Oct 2016 15:45:28 +0100 Subject: [PATCH 157/175] Fix Accept header in tutorial. Closes #4604. [ci skip] --- docs/tutorial/7-schemas-and-client-libraries.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorial/7-schemas-and-client-libraries.md b/docs/tutorial/7-schemas-and-client-libraries.md index 705b79da6..eb1982955 100644 --- a/docs/tutorial/7-schemas-and-client-libraries.md +++ b/docs/tutorial/7-schemas-and-client-libraries.md @@ -53,10 +53,10 @@ representation become available as an option. We can also request the schema from the command line, by specifying the desired content type in the `Accept` header. - $ http http://127.0.0.1:8000/schema/ Accept:application/vnd.coreapi+json + $ http http://127.0.0.1:8000/schema/ Accept:application/coreapi+json HTTP/1.0 200 OK Allow: GET, HEAD, OPTIONS - Content-Type: application/vnd.coreapi+json + Content-Type: application/coreapi+json { "_meta": { From 0fe0e1e7038e74f071e93a77bd5900dc191b086d Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 21 Oct 2016 16:59:34 +0100 Subject: [PATCH 158/175] Fix schema base paths (#4611) --- rest_framework/schemas.py | 15 +++++++++++++-- tests/test_schemas.py | 11 +++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py index af861426c..9b9984699 100644 --- a/rest_framework/schemas.py +++ b/rest_framework/schemas.py @@ -1,4 +1,3 @@ -import os import re from collections import OrderedDict from importlib import import_module @@ -37,6 +36,18 @@ types_lookup = ClassLookupDict({ }) +def common_path(paths): + split_paths = [path.strip('/').split('/') for path in paths] + s1 = min(split_paths) + s2 = max(split_paths) + common = s1 + for i, c in enumerate(s1): + if c != s2[i]: + common = s1[:i] + break + return '/' + '/'.join(common) + + def get_pk_name(model): meta = model._meta.concrete_model._meta return _get_pk(meta).name @@ -292,7 +303,7 @@ class SchemaGenerator(object): # one URL that doesn't have a path prefix. return '/' prefixes.append('/' + prefix + '/') - return os.path.commonprefix(prefixes) + return common_path(prefixes) def create_view(self, callback, method, request=None): """ diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 7188087c4..80b456ea0 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -335,3 +335,14 @@ class TestSchemaGeneratorNotAtRoot(TestCase): } ) self.assertEqual(schema, expected) + + +@unittest.skipUnless(coreapi, 'coreapi is not installed') +class Test4605Regression(TestCase): + def test_4605_regression(self): + generator = SchemaGenerator() + prefix = generator.determine_path_prefix([ + '/api/v1/items/', + '/auth/convert-token/' + ]) + assert prefix == '/' From 30bf9df5d0dc983d180000e413ec2254d7946ff8 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 21 Oct 2016 16:59:43 +0100 Subject: [PATCH 159/175] Fix guardian import (#4612) --- rest_framework/compat.py | 1 - rest_framework/filters.py | 7 ++++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 2d6e7843c..b0e076203 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -207,7 +207,6 @@ guardian = None try: if 'guardian' in settings.INSTALLED_APPS: import guardian - import guardian.shortcuts # Fixes #1624 except ImportError: pass diff --git a/rest_framework/filters.py b/rest_framework/filters.py index f55297b39..47d9a0342 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -289,6 +289,11 @@ class DjangoObjectPermissionsFilter(BaseFilterBackend): perm_format = '%(app_label)s.view_%(model_name)s' def filter_queryset(self, request, queryset, view): + # We want to defer this import until run-time, rather than import-time. + # See https://github.com/tomchristie/django-rest-framework/issues/4608 + # (Also see #1624 for why we need to make this import explicitly) + from guardian.shortcuts import get_objects_for_user + extra = {} user = request.user model_cls = queryset.model @@ -302,4 +307,4 @@ class DjangoObjectPermissionsFilter(BaseFilterBackend): extra = {'accept_global_perms': False} else: extra = {} - return guardian.shortcuts.get_objects_for_user(user, permission, queryset, **extra) + return get_objects_for_user(user, permission, queryset, **extra) From 3b39d2d13a227b1f159663530ad754b183b7225c Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 21 Oct 2016 17:10:38 +0100 Subject: [PATCH 160/175] Version 3.5.1 [ci skip] --- docs/topics/release-notes.md | 21 +++++++++++++++++++++ rest_framework/__init__.py | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 30244f6d2..3d3935684 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,6 +40,15 @@ You can determine your currently installed version using `pip freeze`: ## 3.5.x series +### 3.5.1 + +**Date**: [21st October 2016][3.5.1-milestone] + +* Make `rest_framework/compat.py` imports. ([#4612][gh4612], [#4608][gh4608], [#4601][gh4601]) +* Fix bug in schema base path generation. ([#4611][gh4611], [#4605][gh4605]) +* Fix broken case of ListSerializer with single item. ([#4609][gh4609], [#4606][gh4606]) +* Remove bare `raise` for Python 3.5 compat. ([#4600][gh4600]) + ### 3.5.0 **Date**: [20th October 2016][3.5.0-milestone] @@ -605,6 +614,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.4.6-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.6+Release%22 [3.4.7-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.7+Release%22 [3.5.0-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.5.0+Release%22 +[3.5.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.5.1+Release%22 [gh2013]: https://github.com/tomchristie/django-rest-framework/issues/2013 @@ -1146,3 +1156,14 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh4465]: https://github.com/tomchristie/django-rest-framework/issues/4465 [gh4462]: https://github.com/tomchristie/django-rest-framework/issues/4462 [gh4458]: https://github.com/tomchristie/django-rest-framework/issues/4458 + + + +[gh4612]: https://github.com/tomchristie/django-rest-framework/issues/4612 +[gh4608]: https://github.com/tomchristie/django-rest-framework/issues/4608 +[gh4601]: https://github.com/tomchristie/django-rest-framework/issues/4601 +[gh4611]: https://github.com/tomchristie/django-rest-framework/issues/4611 +[gh4605]: https://github.com/tomchristie/django-rest-framework/issues/4605 +[gh4609]: https://github.com/tomchristie/django-rest-framework/issues/4609 +[gh4606]: https://github.com/tomchristie/django-rest-framework/issues/4606 +[gh4600]: https://github.com/tomchristie/django-rest-framework/issues/4600 diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 68e96703f..0a520bf80 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ ______ _____ _____ _____ __ """ __title__ = 'Django REST framework' -__version__ = '3.5.0' +__version__ = '3.5.1' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2016 Tom Christie' From 8ac524915ca3759d7728206da101b63c6f33b6d3 Mon Sep 17 00:00:00 2001 From: Mads Jensen Date: Sat, 22 Oct 2016 17:37:23 +0200 Subject: [PATCH 161/175] added on_delete=models.CASCADE to models.ForeignKey in the documentation (#4614) --- docs/api-guide/relations.md | 6 +++--- docs/tutorial/4-authentication-and-permissions.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md index aeefae8b1..aabe49412 100644 --- a/docs/api-guide/relations.md +++ b/docs/api-guide/relations.md @@ -39,7 +39,7 @@ In order to explain the various types of relational fields, we'll use a couple o artist = models.CharField(max_length=100) class Track(models.Model): - album = models.ForeignKey(Album, related_name='tracks') + album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE) order = models.IntegerField() title = models.CharField(max_length=100) duration = models.IntegerField() @@ -484,7 +484,7 @@ Note that reverse relationships are not automatically included by the `ModelSeri You'll normally want to ensure that you've set an appropriate `related_name` argument on the relationship, that you can use as the field name. For example: class Track(models.Model): - album = models.ForeignKey(Album, related_name='tracks') + album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE) ... If you have not set a related name for the reverse relationship, you'll need to use the automatically generated related name in the `fields` argument. For example: @@ -508,7 +508,7 @@ For example, given the following model for a tag, which has a generic relationsh See: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/ """ tag_name = models.SlugField() - content_type = models.ForeignKey(ContentType) + content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() tagged_object = GenericForeignKey('content_type', 'object_id') diff --git a/docs/tutorial/4-authentication-and-permissions.md b/docs/tutorial/4-authentication-and-permissions.md index 098194c29..43ccf9186 100644 --- a/docs/tutorial/4-authentication-and-permissions.md +++ b/docs/tutorial/4-authentication-and-permissions.md @@ -14,7 +14,7 @@ First, let's add a couple of fields. One of those fields will be used to repres Add the following two fields to the `Snippet` model in `models.py`. - owner = models.ForeignKey('auth.User', related_name='snippets') + owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE) highlighted = models.TextField() We'd also need to make sure that when the model is saved, that we populate the highlighted field, using the `pygments` code highlighting library. From 72dc6d1d5cae32353e87b7724e666afbd8f04312 Mon Sep 17 00:00:00 2001 From: Phil Krylov Date: Sun, 23 Oct 2016 04:36:36 +0300 Subject: [PATCH 162/175] Add `drf-proxy-pagination` reference to Pagination docs --- docs/api-guide/pagination.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md index f990128c5..f82614eca 100644 --- a/docs/api-guide/pagination.md +++ b/docs/api-guide/pagination.md @@ -321,9 +321,14 @@ 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. +## drf-proxy-pagination + +The [`drf-proxy-pagination` package][drf-proxy-pagination] includes a `ProxyPagination` class which allows to choose pagination class with a query parameter. + [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 +[drf-proxy-pagination]: https://github.com/tuffnatty/drf-proxy-pagination [disqus-cursor-api]: http://cramer.io/2011/03/08/building-cursors-for-the-disqus-api From eafc9a2393139445815feafe0392c170f1172532 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Tue, 25 Oct 2016 15:47:24 -0400 Subject: [PATCH 163/175] Fix is_simple_callable with variable args, kwargs (#4622) --- rest_framework/fields.py | 9 +++++++-- tests/test_fields.py | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index e48285005..f75fcfe05 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -54,12 +54,17 @@ if six.PY3: """ True if the object is a callable that takes no arguments. """ - if not callable(obj): + if not (inspect.isfunction(obj) or inspect.ismethod(obj)): return False sig = inspect.signature(obj) params = sig.parameters.values() - return all(param.default != param.empty for param in params) + return all( + param.kind == param.VAR_POSITIONAL or + param.kind == param.VAR_KEYWORD or + param.default != param.empty + for param in params + ) else: def is_simple_callable(obj): diff --git a/tests/test_fields.py b/tests/test_fields.py index 6fea249ba..92030e3ca 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -37,6 +37,9 @@ class TestIsSimpleCallable: def valid_kwargs(self, param='value'): pass + def valid_vargs_kwargs(self, *args, **kwargs): + pass + def invalid(self, param): pass @@ -45,11 +48,13 @@ class TestIsSimpleCallable: # unbound methods assert not is_simple_callable(Foo.valid) assert not is_simple_callable(Foo.valid_kwargs) + assert not is_simple_callable(Foo.valid_vargs_kwargs) assert not is_simple_callable(Foo.invalid) # bound methods assert is_simple_callable(Foo().valid) assert is_simple_callable(Foo().valid_kwargs) + assert is_simple_callable(Foo().valid_vargs_kwargs) assert not is_simple_callable(Foo().invalid) def test_function(self): @@ -59,13 +64,31 @@ class TestIsSimpleCallable: def valid(param='value', param2='value'): pass + def valid_vargs_kwargs(*args, **kwargs): + pass + def invalid(param, param2='value'): pass assert is_simple_callable(simple) assert is_simple_callable(valid) + assert is_simple_callable(valid_vargs_kwargs) assert not is_simple_callable(invalid) + def test_4602_regression(self): + from django.db import models + + class ChoiceModel(models.Model): + choice_field = models.CharField( + max_length=1, default='a', + choices=(('a', 'A'), ('b', 'B')), + ) + + class Meta: + app_label = 'tests' + + assert is_simple_callable(ChoiceModel().get_choice_field_display) + @unittest.skipUnless(typings, 'requires python 3.5') def test_type_annotation(self): # The annotation will otherwise raise a syntax error in python < 3.5 From 46f837a9d13d5f02fabfa49aef33c16c561a68cf Mon Sep 17 00:00:00 2001 From: Josep Cugat Date: Fri, 28 Oct 2016 13:05:32 +0200 Subject: [PATCH 164/175] Fix APIException full_details() typo in documentation (#4633) APIException has a get_full_details() method but the documentation refers to full_details(). --- docs/api-guide/exceptions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/exceptions.md b/docs/api-guide/exceptions.md index f0f178d92..df8cad42d 100644 --- a/docs/api-guide/exceptions.md +++ b/docs/api-guide/exceptions.md @@ -119,7 +119,7 @@ The available attributes and methods are: * `.detail` - Return the textual description of the error. * `.get_codes()` - Return the code identifier of the error. -* `.full_details()` - Return both the textual description and the code identifier. +* `.get_full_details()` - Return both the textual description and the code identifier. In most cases the error detail will be a simple item: @@ -127,7 +127,7 @@ In most cases the error detail will be a simple item: You do not have permission to perform this action. >>> print(exc.get_codes()) permission_denied - >>> print(exc.full_details()) + >>> print(exc.get_full_details()) {'message':'You do not have permission to perform this action.','code':'permission_denied'} In the case of validation errors the error detail will be either a list or From 895c67c9a22eff1057703a8c578c4833f63a9d70 Mon Sep 17 00:00:00 2001 From: Alex Kahan Date: Mon, 31 Oct 2016 16:41:54 -0400 Subject: [PATCH 165/175] Fixes #4532 (#4636) --- .../rest_framework/admin/dict_value.html | 11 +++ tests/test_templatetags.py | 75 +++++++++++++++---- 2 files changed, 73 insertions(+), 13 deletions(-) diff --git a/rest_framework/templates/rest_framework/admin/dict_value.html b/rest_framework/templates/rest_framework/admin/dict_value.html index e69de29bb..3392c901b 100644 --- a/rest_framework/templates/rest_framework/admin/dict_value.html +++ b/rest_framework/templates/rest_framework/admin/dict_value.html @@ -0,0 +1,11 @@ +{% load rest_framework %} + + + {% for key, value in value.items %} + + + + + {% endfor %} + +
{{ key|format_value }}{{ value|format_value }}
diff --git a/tests/test_templatetags.py b/tests/test_templatetags.py index 28390320b..cac1abf50 100644 --- a/tests/test_templatetags.py +++ b/tests/test_templatetags.py @@ -41,6 +41,9 @@ class TemplateTagTests(TestCase): self.assertEqual(format_value(None), 'null') def test_format_value_hyperlink(self): + """ + Tests format_value with a URL + """ url = 'http://url.com' name = 'name_of_url' hyperlink = Hyperlink(url, name) @@ -54,6 +57,25 @@ class TemplateTagTests(TestCase): self.assertEqual(format_value(list_items), '\n item1, item2, item3\n') self.assertEqual(format_value([]), '\n\n') + def test_format_value_dict(self): + """ + Tests format_value with a dict + """ + test_dict = {'a': 'b'} + expected_dict_format = """ + + + + + + + +
ab
""" + self.assertEqual( + format_html(format_value(test_dict)), + format_html(expected_dict_format) + ) + def test_format_value_table(self): """ Tests format_value with a list of lists/dicts @@ -84,20 +106,47 @@ class TemplateTagTests(TestCase): expected_dict_format = """ - - 0 - - - - 1 - - - - 2 - - + + 0 + + + + + item1 + value1 + + + + + + + 1 + + + + + item2 + value2 + + + + + + + 2 + + + + + item3 + value3 + + + + + - """ + """ list_of_dicts = [{'item1': 'value1'}, {'item2': 'value2'}, {'item3': 'value3'}] self.assertEqual( From 7eb6cdca0080e0f321e660c48fa1d4ac5a6e7dab Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 1 Nov 2016 10:22:30 +0000 Subject: [PATCH 166/175] Don't lose exception info (#4638) --- rest_framework/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/views.py b/rest_framework/views.py index 07575fba7..a8710c7a0 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -445,7 +445,7 @@ class APIView(View): renderer_format = getattr(request.accepted_renderer, 'format') use_plaintext_traceback = renderer_format not in ('html', 'api', 'admin') request.force_plaintext_errors(use_plaintext_traceback) - raise exc + raise # Note: Views are made CSRF exempt from within `as_view` as to prevent # accidental removal of this exemption in cases where `dispatch` needs to From 5c54b227c11688f12ce17f8aa9575e4ab52463d3 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 1 Nov 2016 10:24:53 +0000 Subject: [PATCH 167/175] Drop redundant requests adapter (#4639) --- rest_framework/test.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/rest_framework/test.py b/rest_framework/test.py index 16b1b4cd5..241f94c91 100644 --- a/rest_framework/test.py +++ b/rest_framework/test.py @@ -106,15 +106,6 @@ if requests is not None: def close(self): pass - class NoExternalRequestsAdapter(requests.adapters.HTTPAdapter): - def send(self, request, *args, **kwargs): - msg = ( - 'RequestsClient refusing to make an outgoing network request ' - 'to "%s". Only "testserver" or hostnames in your ALLOWED_HOSTS ' - 'setting are valid.' % request.url - ) - raise RuntimeError(msg) - class RequestsClient(requests.Session): def __init__(self, *args, **kwargs): super(RequestsClient, self).__init__(*args, **kwargs) From d92b24a0b74f681a538f6faff59bb00c6cd447e2 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Tue, 1 Nov 2016 06:27:11 -0400 Subject: [PATCH 168/175] Make serializer fields import explicit (#4628) --- rest_framework/serializers.py | 29 +++++++++++++++++++--- tests/test_serializer.py | 45 ++++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 39987cd07..1bdcd12c3 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -12,18 +12,27 @@ response content is handled by parsers and renderers. """ from __future__ import unicode_literals +import copy +import inspect import traceback +from collections import OrderedDict +from django.core.exceptions import ValidationError as DjangoValidationError +from django.core.exceptions import ImproperlyConfigured from django.db import models from django.db.models import DurationField as ModelDurationField from django.db.models.fields import Field as DjangoModelField from django.db.models.fields import FieldDoesNotExist +from django.utils import six, timezone from django.utils.functional import cached_property from django.utils.translation import ugettext_lazy as _ from rest_framework.compat import JSONField as ModelJSONField from rest_framework.compat import postgres_fields, set_many, unicode_to_repr -from rest_framework.utils import model_meta +from rest_framework.exceptions import ErrorDetail, ValidationError +from rest_framework.fields import get_error_detail, set_value +from rest_framework.settings import api_settings +from rest_framework.utils import html, model_meta, representation from rest_framework.utils.field_mapping import ( ClassLookupDict, get_field_kwargs, get_nested_relation_kwargs, get_relation_kwargs, get_url_kwargs @@ -42,9 +51,23 @@ from rest_framework.validators import ( # # This helps keep the separation between model fields, form fields, and # serializer fields more explicit. +from rest_framework.fields import ( # NOQA # isort:skip + BooleanField, CharField, ChoiceField, DateField, DateTimeField, DecimalField, + DictField, DurationField, EmailField, Field, FileField, FilePathField, FloatField, + HiddenField, IPAddressField, ImageField, IntegerField, JSONField, ListField, + ModelField, MultipleChoiceField, NullBooleanField, ReadOnlyField, RegexField, + SerializerMethodField, SlugField, TimeField, URLField, UUIDField, +) +from rest_framework.relations import ( # NOQA # isort:skip + HyperlinkedIdentityField, HyperlinkedRelatedField, ManyRelatedField, + PrimaryKeyRelatedField, RelatedField, SlugRelatedField, StringRelatedField, +) -from rest_framework.fields import * # NOQA # isort:skip -from rest_framework.relations import * # NOQA # isort:skip +# Non-field imports, but public API +from rest_framework.fields import ( # NOQA # isort:skip + CreateOnlyDefault, CurrentUserDefault, SkipField, empty +) +from rest_framework.relations import Hyperlink, PKOnlyObject # NOQA # isort:skip # We assume that 'validators' are intended for the child serializer, # rather than the parent serializer. diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 32be39faa..8c8b5b163 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -1,17 +1,60 @@ # coding: utf-8 from __future__ import unicode_literals +import inspect import pickle import re import pytest -from rest_framework import serializers +from rest_framework import fields, relations, serializers from rest_framework.compat import unicode_repr +from rest_framework.fields import Field from .utils import MockObject +# Test serializer fields imports. +# ------------------------------- + +class TestFieldImports: + def is_field(self, name, value): + return ( + isinstance(value, type) and + issubclass(value, Field) and + not name.startswith('_') + ) + + def test_fields(self): + msg = "Expected `fields.%s` to be imported in `serializers`" + field_classes = [ + key for key, value + in inspect.getmembers(fields) + if self.is_field(key, value) + ] + + # sanity check + assert 'Field' in field_classes + assert 'BooleanField' in field_classes + + for field in field_classes: + assert hasattr(serializers, field), msg % field + + def test_relations(self): + msg = "Expected `relations.%s` to be imported in `serializers`" + field_classes = [ + key for key, value + in inspect.getmembers(relations) + if self.is_field(key, value) + ] + + # sanity check + assert 'RelatedField' in field_classes + + for field in field_classes: + assert hasattr(serializers, field), msg % field + + # Tests for core functionality. # ----------------------------- From 98df932194722d6fc81becedc55eb695a32f925f Mon Sep 17 00:00:00 2001 From: Kieran Spear Date: Tue, 1 Nov 2016 18:30:17 +0800 Subject: [PATCH 169/175] Fix FilterSet proxy (#4620) --- rest_framework/filters.py | 35 ++++++++++++++++++++++++++--------- tests/test_filters.py | 23 +++++++++++++++++++++++ 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 47d9a0342..531531efc 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -37,15 +37,32 @@ class BaseFilterBackend(object): return [] -class FilterSet(object): - def __new__(cls, *args, **kwargs): - warnings.warn( - "The built in 'rest_framework.filters.FilterSet' is pending deprecation. " - "You should use 'django_filters.rest_framework.FilterSet' instead.", - PendingDeprecationWarning - ) - from django_filters.rest_framework import FilterSet - return FilterSet(*args, **kwargs) +if django_filters: + from django_filters.filterset import FilterSetMetaclass as DFFilterSetMetaclass + from django_filters.rest_framework.filterset import FilterSet as DFFilterSet + + class FilterSetMetaclass(DFFilterSetMetaclass): + def __new__(cls, name, bases, attrs): + warnings.warn( + "The built in 'rest_framework.filters.FilterSet' is pending deprecation. " + "You should use 'django_filters.rest_framework.FilterSet' instead.", + PendingDeprecationWarning + ) + return super(FilterSetMetaclass, cls).__new__(cls, name, bases, attrs) + _BaseFilterSet = DFFilterSet +else: + # Dummy metaclass just so we can give a user-friendly error message. + class FilterSetMetaclass(type): + def __init__(self, name, bases, attrs): + # Assert only on subclasses, so we can define FilterSet below. + if bases != (object,): + assert False, 'django-filter must be installed to use the `FilterSet` class' + super(FilterSetMetaclass, self).__init__(name, bases, attrs) + _BaseFilterSet = object + + +class FilterSet(six.with_metaclass(FilterSetMetaclass, _BaseFilterSet)): + pass class DjangoFilterBackend(BaseFilterBackend): diff --git a/tests/test_filters.py b/tests/test_filters.py index 9795230d6..12fb85895 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -79,12 +79,23 @@ if django_filters: model = BaseFilterableItem fields = '__all__' + # Test the same filter using the deprecated internal FilterSet class. + class BaseFilterableItemFilterWithProxy(filters.FilterSet): + text = django_filters.CharFilter() + + class Meta: + model = BaseFilterableItem + fields = '__all__' + class BaseFilterableItemFilterRootView(generics.ListCreateAPIView): queryset = FilterableItem.objects.all() serializer_class = FilterableItemSerializer filter_class = BaseFilterableItemFilter filter_backends = (filters.DjangoFilterBackend,) + class BaseFilterableItemFilterWithProxyRootView(BaseFilterableItemFilterRootView): + filter_class = BaseFilterableItemFilterWithProxy + # Regression test for #814 class FilterFieldsQuerysetView(generics.ListCreateAPIView): queryset = FilterableItem.objects.all() @@ -296,6 +307,18 @@ class IntegrationTestFiltering(CommonFilteringTestCase): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.data), 1) + @unittest.skipUnless(django_filters, 'django-filter not installed') + def test_base_model_filter_with_proxy(self): + """ + The `get_filter_class` model checks should allow base model filters. + """ + view = BaseFilterableItemFilterWithProxyRootView.as_view() + + request = factory.get('/?text=aaa') + response = view(request).render() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data), 1) + @unittest.skipUnless(django_filters, 'django-filter not installed') def test_unknown_filter(self): """ From 97d848413e5e13e9de83b7e840fc719b9ec0d9c7 Mon Sep 17 00:00:00 2001 From: Nicolas Delaby Date: Tue, 1 Nov 2016 11:38:56 +0100 Subject: [PATCH 170/175] Fix support of get_full_details() for Throttled exceptions (#4627) Since `str` objects are immutable, appending to existing `str` creates in fact a new `str` instance. Thus `ErrorDetail.detail.code` attribute is lost after `str` concatenation operation. --- rest_framework/exceptions.py | 20 ++++++++++---------- tests/test_exceptions.py | 20 +++++++++++++++++++- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py index e41655fef..e84074a07 100644 --- a/rest_framework/exceptions.py +++ b/rest_framework/exceptions.py @@ -210,14 +210,14 @@ class Throttled(APIException): default_code = 'throttled' def __init__(self, wait=None, detail=None, code=None): + if detail is None: + detail = force_text(self.default_detail) + if wait is not None: + wait = math.ceil(wait) + detail = ' '.join(( + detail, + force_text(ungettext(self.extra_detail_singular.format(wait=wait), + self.extra_detail_plural.format(wait=wait), + wait)))) + self.wait = wait super(Throttled, self).__init__(detail, code) - - if wait is None: - self.wait = None - else: - self.wait = math.ceil(wait) - self.detail += ' ' + force_text(ungettext( - self.extra_detail_singular.format(wait=self.wait), - self.extra_detail_plural.format(wait=self.wait), - self.wait - )) diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 29703cb77..f1d172211 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -1,9 +1,12 @@ from __future__ import unicode_literals from django.test import TestCase +from django.utils import six from django.utils.translation import ugettext_lazy as _ -from rest_framework.exceptions import ErrorDetail, _get_error_details +from rest_framework.exceptions import ( + ErrorDetail, Throttled, _get_error_details +) class ExceptionTestCase(TestCase): @@ -39,3 +42,18 @@ class ExceptionTestCase(TestCase): _get_error_details([[lazy_example]])[0][0], ErrorDetail ) + + def test_get_full_details_with_throttling(self): + exception = Throttled() + assert exception.get_full_details() == { + 'message': 'Request was throttled.', 'code': 'throttled'} + + exception = Throttled(wait=2) + assert exception.get_full_details() == { + 'message': 'Request was throttled. Expected available in {} seconds.'.format(2 if six.PY3 else 2.), + 'code': 'throttled'} + + exception = Throttled(wait=2, detail='Slow down!') + assert exception.get_full_details() == { + 'message': 'Slow down! Expected available in {} seconds.'.format(2 if six.PY3 else 2.), + 'code': 'throttled'} From 70385711572e7ea141644f349b7180c68b4c15d2 Mon Sep 17 00:00:00 2001 From: Kennedy Mwenja Date: Tue, 1 Nov 2016 13:42:01 +0300 Subject: [PATCH 171/175] Enable cursor pagination of value querysets. (#4569) To do `GROUP_BY` queries in django requires one to use `.values()` eg this groups posts by user getting a count of posts per user. ``` Posts.objects.order_by('user').values('user').annotate(post_count=Count('post')) ``` This would produce a value queryset which serializes its result objects as dictionaries while `CursorPagination` requires a queryset with result objects that are model instances. This commit enables cursor pagination for value querysets. - had to mangle the tests a bit to test it out. They might need some refactoring. - tried the same for `.values_list()` but it turned out to be trickier than I expected since you have to use tuple indexes. --- rest_framework/pagination.py | 6 +- tests/test_pagination.py | 221 ++++++++++++++++++++++------------- 2 files changed, 147 insertions(+), 80 deletions(-) diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 708da29cd..8ccdc342c 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -711,7 +711,11 @@ class CursorPagination(BasePagination): return replace_query_param(self.base_url, self.cursor_query_param, encoded) def _get_position_from_instance(self, instance, ordering): - attr = getattr(instance, ordering[0].lstrip('-')) + field_name = ordering[0].lstrip('-') + if isinstance(instance, dict): + attr = instance[field_name] + else: + attr = getattr(instance, field_name) return six.text_type(attr) def get_paginated_response(self, data): diff --git a/tests/test_pagination.py b/tests/test_pagination.py index 170d95899..9f2e1c57c 100644 --- a/tests/test_pagination.py +++ b/tests/test_pagination.py @@ -3,6 +3,8 @@ from __future__ import unicode_literals import pytest from django.core.paginator import Paginator as DjangoPaginator +from django.db import models +from django.test import TestCase from rest_framework import ( exceptions, filters, generics, pagination, serializers, status @@ -530,85 +532,7 @@ class TestLimitOffset: assert content.get('previous') == prev_url -class TestCursorPagination: - """ - Unit tests for `pagination.CursorPagination`. - """ - - def setup(self): - class MockObject(object): - def __init__(self, idx): - self.created = idx - - class MockQuerySet(object): - def __init__(self, items): - self.items = items - - def filter(self, created__gt=None, created__lt=None): - if created__gt is not None: - return MockQuerySet([ - item for item in self.items - if item.created > int(created__gt) - ]) - - assert created__lt is not None - return MockQuerySet([ - item for item in self.items - if item.created < int(created__lt) - ]) - - def order_by(self, *ordering): - if ordering[0].startswith('-'): - return MockQuerySet(list(reversed(self.items))) - return self - - def __getitem__(self, sliced): - return self.items[sliced] - - class ExamplePagination(pagination.CursorPagination): - page_size = 5 - ordering = 'created' - - self.pagination = ExamplePagination() - self.queryset = MockQuerySet([ - MockObject(idx) for idx in [ - 1, 1, 1, 1, 1, - 1, 2, 3, 4, 4, - 4, 4, 5, 6, 7, - 7, 7, 7, 7, 7, - 7, 7, 7, 8, 9, - 9, 9, 9, 9, 9 - ] - ]) - - def get_pages(self, url): - """ - Given a URL return a tuple of: - - (previous page, current page, next page, previous url, next url) - """ - request = Request(factory.get(url)) - queryset = self.pagination.paginate_queryset(self.queryset, request) - current = [item.created for item in queryset] - - next_url = self.pagination.get_next_link() - previous_url = self.pagination.get_previous_link() - - if next_url is not None: - request = Request(factory.get(next_url)) - queryset = self.pagination.paginate_queryset(self.queryset, request) - next = [item.created for item in queryset] - else: - next = None - - if previous_url is not None: - request = Request(factory.get(previous_url)) - queryset = self.pagination.paginate_queryset(self.queryset, request) - previous = [item.created for item in queryset] - else: - previous = None - - return (previous, current, next, previous_url, next_url) +class CursorPaginationTestsMixin: def test_invalid_cursor(self): request = Request(factory.get('/', {'cursor': '123'})) @@ -703,6 +627,145 @@ class TestCursorPagination: assert isinstance(self.pagination.to_html(), type('')) +class TestCursorPagination(CursorPaginationTestsMixin): + """ + Unit tests for `pagination.CursorPagination`. + """ + + def setup(self): + class MockObject(object): + def __init__(self, idx): + self.created = idx + + class MockQuerySet(object): + def __init__(self, items): + self.items = items + + def filter(self, created__gt=None, created__lt=None): + if created__gt is not None: + return MockQuerySet([ + item for item in self.items + if item.created > int(created__gt) + ]) + + assert created__lt is not None + return MockQuerySet([ + item for item in self.items + if item.created < int(created__lt) + ]) + + def order_by(self, *ordering): + if ordering[0].startswith('-'): + return MockQuerySet(list(reversed(self.items))) + return self + + def __getitem__(self, sliced): + return self.items[sliced] + + class ExamplePagination(pagination.CursorPagination): + page_size = 5 + ordering = 'created' + + self.pagination = ExamplePagination() + self.queryset = MockQuerySet([ + MockObject(idx) for idx in [ + 1, 1, 1, 1, 1, + 1, 2, 3, 4, 4, + 4, 4, 5, 6, 7, + 7, 7, 7, 7, 7, + 7, 7, 7, 8, 9, + 9, 9, 9, 9, 9 + ] + ]) + + def get_pages(self, url): + """ + Given a URL return a tuple of: + + (previous page, current page, next page, previous url, next url) + """ + request = Request(factory.get(url)) + queryset = self.pagination.paginate_queryset(self.queryset, request) + current = [item.created for item in queryset] + + next_url = self.pagination.get_next_link() + previous_url = self.pagination.get_previous_link() + + if next_url is not None: + request = Request(factory.get(next_url)) + queryset = self.pagination.paginate_queryset(self.queryset, request) + next = [item.created for item in queryset] + else: + next = None + + if previous_url is not None: + request = Request(factory.get(previous_url)) + queryset = self.pagination.paginate_queryset(self.queryset, request) + previous = [item.created for item in queryset] + else: + previous = None + + return (previous, current, next, previous_url, next_url) + + +class CursorPaginationModel(models.Model): + created = models.IntegerField() + + +class TestCursorPaginationWithValueQueryset(CursorPaginationTestsMixin, TestCase): + """ + Unit tests for `pagination.CursorPagination` for value querysets. + """ + + def setUp(self): + class ExamplePagination(pagination.CursorPagination): + page_size = 5 + ordering = 'created' + + self.pagination = ExamplePagination() + data = [ + 1, 1, 1, 1, 1, + 1, 2, 3, 4, 4, + 4, 4, 5, 6, 7, + 7, 7, 7, 7, 7, + 7, 7, 7, 8, 9, + 9, 9, 9, 9, 9 + ] + for idx in data: + CursorPaginationModel.objects.create(created=idx) + + self.queryset = CursorPaginationModel.objects.values() + + def get_pages(self, url): + """ + Given a URL return a tuple of: + + (previous page, current page, next page, previous url, next url) + """ + request = Request(factory.get(url)) + queryset = self.pagination.paginate_queryset(self.queryset, request) + current = [item['created'] for item in queryset] + + next_url = self.pagination.get_next_link() + previous_url = self.pagination.get_previous_link() + + if next_url is not None: + request = Request(factory.get(next_url)) + queryset = self.pagination.paginate_queryset(self.queryset, request) + next = [item['created'] for item in queryset] + else: + next = None + + if previous_url is not None: + request = Request(factory.get(previous_url)) + queryset = self.pagination.paginate_queryset(self.queryset, request) + previous = [item['created'] for item in queryset] + else: + previous = None + + return (previous, current, next, previous_url, next_url) + + def test_get_displayed_page_numbers(): """ Test our contextual page display function. From 276ed80fd302184dddb3af01e53d43be4aef15e4 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 1 Nov 2016 11:11:34 +0000 Subject: [PATCH 172/175] Support 'on'/'off' literals with BooleanField. Closes #4624 (#4640) --- rest_framework/fields.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index f75fcfe05..13b5145ba 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -644,8 +644,20 @@ class BooleanField(Field): } default_empty_html = False initial = False - TRUE_VALUES = {'t', 'T', 'true', 'True', 'TRUE', '1', 1, True} - FALSE_VALUES = {'f', 'F', 'false', 'False', 'FALSE', '0', 0, 0.0, False} + TRUE_VALUES = { + 't', 'T', + 'true', 'True', 'TRUE', + 'on', 'On', 'ON', + '1', 1, + True + } + FALSE_VALUES = { + 'f', 'F', + 'false', 'False', 'FALSE', + 'off', 'Off', 'OFF', + '0', 0, 0.0, + False + } def __init__(self, **kwargs): assert 'allow_null' not in kwargs, '`allow_null` is not a valid option. Use `NullBooleanField` instead.' From 2bf082a6236cf979d2d125f1f5a60f18509323fc Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 1 Nov 2016 11:31:20 +0000 Subject: [PATCH 173/175] Version 3.5.2 [ci skip] (#4641) --- docs/topics/release-notes.md | 31 +++++++++++++++++++++++++++++++ rest_framework/__init__.py | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 3d3935684..d25a46ba0 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -40,6 +40,20 @@ You can determine your currently installed version using `pip freeze`: ## 3.5.x series +### 3.5.2 + +**Date**: [1st November 2016][3.5.2-milestone] + +* Restore exception tracebacks in Python 2.7. ([#4631][gh4631], [#4638][gh4638]) +* Properly display dicts in the admin console. ([#4532][gh4532], [#4636][gh4636]) +* Fix is_simple_callable with variable args, kwargs. ([#4622][gh4622], [#4602][gh4602]) +* Support 'on'/'off' literals with BooleanField. ([#4640][gh4640], [#4624][gh4624]) +* Enable cursor pagination of value querysets. ([#4569][gh4569]) +* Fix support of get_full_details() for Throttled exceptions. ([#4627][gh4627]) +* Fix FilterSet proxy. ([#4620][gh4620]) +* Make serializer fields import explicit. ([#4628][gh4628]) +* Drop redundant requests adapter. ([#4639][gh4639]) + ### 3.5.1 **Date**: [21st October 2016][3.5.1-milestone] @@ -615,6 +629,7 @@ For older release notes, [please see the version 2.x documentation][old-release- [3.4.7-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.4.7+Release%22 [3.5.0-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.5.0+Release%22 [3.5.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.5.1+Release%22 +[3.5.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.5.2+Release%22 [gh2013]: https://github.com/tomchristie/django-rest-framework/issues/2013 @@ -1167,3 +1182,19 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh4609]: https://github.com/tomchristie/django-rest-framework/issues/4609 [gh4606]: https://github.com/tomchristie/django-rest-framework/issues/4606 [gh4600]: https://github.com/tomchristie/django-rest-framework/issues/4600 + + + +[gh4631]: https://github.com/tomchristie/django-rest-framework/issues/4631 +[gh4638]: https://github.com/tomchristie/django-rest-framework/issues/4638 +[gh4532]: https://github.com/tomchristie/django-rest-framework/issues/4532 +[gh4636]: https://github.com/tomchristie/django-rest-framework/issues/4636 +[gh4622]: https://github.com/tomchristie/django-rest-framework/issues/4622 +[gh4602]: https://github.com/tomchristie/django-rest-framework/issues/4602 +[gh4640]: https://github.com/tomchristie/django-rest-framework/issues/4640 +[gh4624]: https://github.com/tomchristie/django-rest-framework/issues/4624 +[gh4569]: https://github.com/tomchristie/django-rest-framework/issues/4569 +[gh4627]: https://github.com/tomchristie/django-rest-framework/issues/4627 +[gh4620]: https://github.com/tomchristie/django-rest-framework/issues/4620 +[gh4628]: https://github.com/tomchristie/django-rest-framework/issues/4628 +[gh4639]: https://github.com/tomchristie/django-rest-framework/issues/4639 diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 0a520bf80..fe82763a9 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ ______ _____ _____ _____ __ """ __title__ = 'Django REST framework' -__version__ = '3.5.1' +__version__ = '3.5.2' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2016 Tom Christie' From 45e058d7bace860300d3edf537b86996aec15c33 Mon Sep 17 00:00:00 2001 From: Andrzej Pragacz Date: Wed, 2 Nov 2016 10:04:01 +0100 Subject: [PATCH 174/175] Fix unhandled Http404, PermissionDenied in schema generation (#4645) (#4646) --- rest_framework/schemas.py | 4 ++- tests/test_schemas.py | 68 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py index 9b9984699..9439cb691 100644 --- a/rest_framework/schemas.py +++ b/rest_framework/schemas.py @@ -4,6 +4,8 @@ from importlib import import_module from django.conf import settings from django.contrib.admindocs.views import simplify_regex +from django.core.exceptions import PermissionDenied +from django.http import Http404 from django.utils import six from django.utils.encoding import force_text, smart_text @@ -339,7 +341,7 @@ class SchemaGenerator(object): try: view.check_permissions(view.request) - except exceptions.APIException: + except (exceptions.APIException, Http404, PermissionDenied): return False return True diff --git a/tests/test_schemas.py b/tests/test_schemas.py index 80b456ea0..e196a1f61 100644 --- a/tests/test_schemas.py +++ b/tests/test_schemas.py @@ -1,17 +1,22 @@ import unittest from django.conf.urls import include, url +from django.core.exceptions import PermissionDenied +from django.http import Http404 from django.test import TestCase, override_settings from rest_framework import filters, pagination, permissions, serializers from rest_framework.compat import coreapi from rest_framework.decorators import detail_route, list_route +from rest_framework.request import Request from rest_framework.routers import DefaultRouter from rest_framework.schemas import SchemaGenerator, get_schema_view -from rest_framework.test import APIClient +from rest_framework.test import APIClient, APIRequestFactory from rest_framework.views import APIView from rest_framework.viewsets import ModelViewSet +factory = APIRequestFactory() + class MockUser(object): def is_authenticated(self): @@ -215,6 +220,32 @@ class TestRouterGeneratedSchema(TestCase): self.assertEqual(response.data, expected) +class DenyAllUsingHttp404(permissions.BasePermission): + + def has_permission(self, request, view): + raise Http404() + + def has_object_permission(self, request, view, obj): + raise Http404() + + +class DenyAllUsingPermissionDenied(permissions.BasePermission): + + def has_permission(self, request, view): + raise PermissionDenied() + + def has_object_permission(self, request, view, obj): + raise PermissionDenied() + + +class Http404ExampleViewSet(ExampleViewSet): + permission_classes = [DenyAllUsingHttp404] + + +class PermissionDeniedExampleViewSet(ExampleViewSet): + permission_classes = [DenyAllUsingPermissionDenied] + + class ExampleListView(APIView): permission_classes = [permissions.IsAuthenticatedOrReadOnly] @@ -337,6 +368,41 @@ class TestSchemaGeneratorNotAtRoot(TestCase): self.assertEqual(schema, expected) +@unittest.skipUnless(coreapi, 'coreapi is not installed') +class TestSchemaGeneratorWithRestrictedViewSets(TestCase): + def setUp(self): + router = DefaultRouter() + router.register('example1', Http404ExampleViewSet, base_name='example1') + router.register('example2', PermissionDeniedExampleViewSet, base_name='example2') + self.patterns = [ + url('^example/?$', ExampleListView.as_view()), + url(r'^', include(router.urls)) + ] + + def test_schema_for_regular_views(self): + """ + Ensure that schema generation works for ViewSet classes + with permission classes raising exceptions. + """ + generator = SchemaGenerator(title='Example API', patterns=self.patterns) + request = factory.get('/') + schema = generator.get_schema(Request(request)) + expected = coreapi.Document( + url='', + title='Example API', + content={ + 'example': { + 'list': coreapi.Link( + url='/example/', + action='get', + fields=[] + ), + }, + } + ) + self.assertEqual(schema, expected) + + @unittest.skipUnless(coreapi, 'coreapi is not installed') class Test4605Regression(TestCase): def test_4605_regression(self): From d55e176a1e882d498b63b46a01f0ac389428f084 Mon Sep 17 00:00:00 2001 From: Carlos de la Torre Date: Wed, 2 Nov 2016 11:03:53 -0300 Subject: [PATCH 175/175] Fix documentation error: removed unused variable (#4647) --- docs/api-guide/fields.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index f986f1508..17168b721 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -626,7 +626,6 @@ The `.fail()` method is a shortcut for raising `ValidationError` that takes a me def to_internal_value(self, data): if not isinstance(data, six.text_type): - msg = 'Incorrect type. Expected a string, but got %s' self.fail('incorrect_type', input_type=type(data).__name__) if not re.match(r'^rgb\([0-9]+,[0-9]+,[0-9]+\)$', data):