From d972df7c9c1867b4a0a57307f423a488c4d4f4b1 Mon Sep 17 00:00:00 2001 From: tanwanirahul Date: Mon, 3 Nov 2014 14:43:53 +0100 Subject: Ability to override default method names by customizing it --- rest_framework/routers.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 169e6e8b..d1c9fa1b 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -176,23 +176,27 @@ class SimpleRouter(BaseRouter): if isinstance(route, DynamicDetailRoute): # Dynamic detail routes (@detail_route decorator) for httpmethods, methodname in detail_routes: + method_kwargs = getattr(viewset, methodname).kwargs + custom_method_name = method_kwargs.pop("custom_method_name", None) or methodname initkwargs = route.initkwargs.copy() - initkwargs.update(getattr(viewset, methodname).kwargs) + initkwargs.update(method_kwargs) ret.append(Route( - url=replace_methodname(route.url, methodname), + url=replace_methodname(route.url, custom_method_name), mapping=dict((httpmethod, methodname) for httpmethod in httpmethods), - name=replace_methodname(route.name, methodname), + name=replace_methodname(route.name, custom_method_name), initkwargs=initkwargs, )) elif isinstance(route, DynamicListRoute): # Dynamic list routes (@list_route decorator) for httpmethods, methodname in list_routes: + method_kwargs = getattr(viewset, methodname).kwargs + custom_method_name = method_kwargs.pop("custom_method_name", None) or methodname initkwargs = route.initkwargs.copy() - initkwargs.update(getattr(viewset, methodname).kwargs) + initkwargs.update(method_kwargs) ret.append(Route( - url=replace_methodname(route.url, methodname), + url=replace_methodname(route.url, custom_method_name), mapping=dict((httpmethod, methodname) for httpmethod in httpmethods), - name=replace_methodname(route.name, methodname), + name=replace_methodname(route.name, custom_method_name), initkwargs=initkwargs, )) else: -- cgit v1.2.3 From 92ebeaa040f75dbc6142355fa25d89b4c990685b Mon Sep 17 00:00:00 2001 From: tanwanirahul Date: Fri, 19 Dec 2014 19:52:59 +0530 Subject: Change decorator attribute name to url_path per suggestions --- rest_framework/routers.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/routers.py b/rest_framework/routers.py index d1c9fa1b..a213f62c 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -177,26 +177,26 @@ class SimpleRouter(BaseRouter): # Dynamic detail routes (@detail_route decorator) for httpmethods, methodname in detail_routes: method_kwargs = getattr(viewset, methodname).kwargs - custom_method_name = method_kwargs.pop("custom_method_name", None) or methodname + url_path = method_kwargs.pop("url_path", None) or methodname initkwargs = route.initkwargs.copy() initkwargs.update(method_kwargs) ret.append(Route( - url=replace_methodname(route.url, custom_method_name), + url=replace_methodname(route.url, url_path), mapping=dict((httpmethod, methodname) for httpmethod in httpmethods), - name=replace_methodname(route.name, custom_method_name), + name=replace_methodname(route.name, url_path), initkwargs=initkwargs, )) elif isinstance(route, DynamicListRoute): # Dynamic list routes (@list_route decorator) for httpmethods, methodname in list_routes: method_kwargs = getattr(viewset, methodname).kwargs - custom_method_name = method_kwargs.pop("custom_method_name", None) or methodname + url_path = method_kwargs.pop("url_path", None) or methodname initkwargs = route.initkwargs.copy() initkwargs.update(method_kwargs) ret.append(Route( - url=replace_methodname(route.url, custom_method_name), + url=replace_methodname(route.url, url_path), mapping=dict((httpmethod, methodname) for httpmethod in httpmethods), - name=replace_methodname(route.name, custom_method_name), + name=replace_methodname(route.name, url_path), initkwargs=initkwargs, )) else: -- cgit v1.2.3 From 2a1485e00943b8280245d19e1e1f8514b1ef18ea Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 19 Dec 2014 21:32:43 +0000 Subject: Final bits of docs for ModelSerializer fields API --- rest_framework/serializers.py | 140 +++++++++++++++++++++---------------- rest_framework/utils/model_meta.py | 10 +-- 2 files changed, 83 insertions(+), 67 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 8adbafe4..623ed586 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -802,10 +802,25 @@ class ModelSerializer(Serializer): Return the dict of field names -> field instances that should be used for `self.fields` when instantiating the serializer. """ + assert hasattr(self, 'Meta'), ( + 'Class {serializer_class} missing "Meta" attribute'.format( + serializer_class=self.__class__.__name__ + ) + ) + assert hasattr(self.Meta, 'model'), ( + 'Class {serializer_class} missing "Meta.model" attribute'.format( + serializer_class=self.__class__.__name__ + ) + ) + declared_fields = copy.deepcopy(self._declared_fields) model = getattr(self.Meta, 'model') depth = getattr(self.Meta, 'depth', 0) + if depth is not None: + assert depth >= 0, "'depth' may not be negative." + assert depth <= 10, "'depth' may not be greater than 10." + # Retrieve metadata about fields & relationships on the model class. info = model_meta.get_field_info(model) field_names = self.get_field_names(declared_fields, info) @@ -817,27 +832,32 @@ class ModelSerializer(Serializer): field_names, declared_fields, extra_kwargs ) - # Now determine the fields that should be included on the serializer. - ret = OrderedDict() + # Determine the fields that should be included on the serializer. + fields = OrderedDict() + for field_name in field_names: + # If the field is explicitly declared on the class then use that. if field_name in declared_fields: - # Field is explicitly declared on the class, use that. - ret[field_name] = declared_fields[field_name] + fields[field_name] = declared_fields[field_name] continue # Determine the serializer field class and keyword arguments. - field_cls, kwargs = self.build_field(field_name, info, model, depth) + field_class, field_kwargs = self.build_field( + field_name, info, model, depth + ) - # Populate any kwargs defined in `Meta.extra_kwargs` - kwargs = self.build_field_kwargs(kwargs, extra_kwargs, field_name) + # Include any kwargs defined in `Meta.extra_kwargs` + field_kwargs = self.build_field_kwargs( + field_kwargs, extra_kwargs, field_name + ) # Create the serializer field. - ret[field_name] = field_cls(**kwargs) + fields[field_name] = field_class(**field_kwargs) # Add in any hidden fields. - ret.update(hidden_fields) + fields.update(hidden_fields) - return ret + return fields # Methods for determining the set of field names to include... @@ -916,108 +936,105 @@ class ModelSerializer(Serializer): # Methods for constructing serializer fields... - def build_field(self, field_name, info, model, nested_depth): + def build_field(self, field_name, info, model_class, nested_depth): """ Return a two tuple of (cls, kwargs) to build a serializer field with. """ if field_name in info.fields_and_pk: - return self.build_standard_field(field_name, info, model) + model_field = info.fields_and_pk[field_name] + return self.build_standard_field(field_name, model_field) elif field_name in info.relations: + relation_info = info.relations[field_name] if not nested_depth: - return self.build_relational_field(field_name, info, model) + return self.build_relational_field(field_name, relation_info) else: - return self.build_nested_field(field_name, info, model, nested_depth) + return self.build_nested_field(field_name, relation_info, nested_depth) - elif hasattr(model, field_name): - return self.build_property_field(field_name, info, model) + elif hasattr(model_class, field_name): + return self.build_property_field(field_name, model_class) elif field_name == api_settings.URL_FIELD_NAME: - return self.build_url_field(field_name, info, model) + return self.build_url_field(field_name, model_class) - return self.build_unknown_field(field_name, info, model) + return self.build_unknown_field(field_name, model_class) - def build_standard_field(self, field_name, info, model): + def build_standard_field(self, field_name, model_field): """ Create regular model fields. """ field_mapping = ClassLookupDict(self.serializer_field_mapping) - model_field = info.fields_and_pk[field_name] - field_cls = field_mapping[model_field] - kwargs = get_field_kwargs(field_name, model_field) + field_class = field_mapping[model_field] + field_kwargs = get_field_kwargs(field_name, model_field) - if 'choices' in kwargs: + if 'choices' in field_kwargs: # Fields with choices get coerced into `ChoiceField` # instead of using their regular typed field. - field_cls = ChoiceField - if not issubclass(field_cls, ModelField): + field_class = ChoiceField + if not issubclass(field_class, ModelField): # `model_field` is only valid for the fallback case of # `ModelField`, which is used when no other typed field # matched to the model field. - kwargs.pop('model_field', None) - if not issubclass(field_cls, CharField) and not issubclass(field_cls, ChoiceField): + field_kwargs.pop('model_field', None) + if not issubclass(field_class, CharField) and not issubclass(field_class, ChoiceField): # `allow_blank` is only valid for textual fields. - kwargs.pop('allow_blank', None) + field_kwargs.pop('allow_blank', None) - return field_cls, kwargs + return field_class, field_kwargs - def build_relational_field(self, field_name, info, model): + def build_relational_field(self, field_name, relation_info): """ Create fields for forward and reverse relationships. """ - relation_info = info.relations[field_name] - - field_cls = self.serializer_related_class - kwargs = get_relation_kwargs(field_name, relation_info) + field_class = self.serializer_related_class + field_kwargs = get_relation_kwargs(field_name, relation_info) # `view_name` is only valid for hyperlinked relationships. - if not issubclass(field_cls, HyperlinkedRelatedField): - kwargs.pop('view_name', None) + if not issubclass(field_class, HyperlinkedRelatedField): + field_kwargs.pop('view_name', None) - return field_cls, kwargs + return field_class, field_kwargs - def build_nested_field(self, field_name, info, model, nested_depth): + def build_nested_field(self, field_name, relation_info, nested_depth): """ Create nested fields for forward and reverse relationships. """ - relation_info = info.relations[field_name] - class NestedSerializer(ModelSerializer): class Meta: - model = relation_info.related - depth = nested_depth - 1 + model = relation_info.related_model + depth = nested_depth - field_cls = NestedSerializer - kwargs = get_nested_relation_kwargs(relation_info) + field_class = NestedSerializer + field_kwargs = get_nested_relation_kwargs(relation_info) - return field_cls, kwargs + return field_class, field_kwargs - def build_property_field(self, field_name, info, model): + def build_property_field(self, field_name, model_class): """ Create a read only field for model methods and properties. """ - field_cls = ReadOnlyField - kwargs = {} + field_class = ReadOnlyField + field_kwargs = {} - return field_cls, kwargs + return field_class, field_kwargs - def build_url_field(self, field_name, info, model): + def build_url_field(self, field_name, model_class): """ Create a field representing the object's own URL. """ - field_cls = HyperlinkedIdentityField - kwargs = get_url_kwargs(model) + field_class = HyperlinkedIdentityField + field_kwargs = get_url_kwargs(model_class) - return field_cls, kwargs + return field_class, field_kwargs - def build_unknown_field(self, field_name, info, model): + def build_unknown_field(self, field_name, model_class): """ Raise an error on any unknown fields. """ raise ImproperlyConfigured( 'Field name `%s` is not valid for model `%s`.' % - (field_name, model.__class__.__name__) + (field_name, model_class.__name__) ) def build_field_kwargs(self, kwargs, extra_kwargs, field_name): @@ -1318,17 +1335,16 @@ class HyperlinkedModelSerializer(ModelSerializer): list(model_info.forward_relations.keys()) ) - def build_nested_field(self, field_name, info, model, nested_depth): + def build_nested_field(self, field_name, relation_info, nested_depth): """ Create nested fields for forward and reverse relationships. """ - relation_info = info.relations[field_name] - class NestedSerializer(HyperlinkedModelSerializer): class Meta: - model = relation_info.related + model = relation_info.related_model depth = nested_depth - 1 - field_cls = NestedSerializer - kwargs = get_nested_relation_kwargs(relation_info) - return field_cls, kwargs + field_class = NestedSerializer + field_kwargs = get_nested_relation_kwargs(relation_info) + + return field_class, field_kwargs diff --git a/rest_framework/utils/model_meta.py b/rest_framework/utils/model_meta.py index c98725c6..dfc387ca 100644 --- a/rest_framework/utils/model_meta.py +++ b/rest_framework/utils/model_meta.py @@ -24,7 +24,7 @@ FieldInfo = namedtuple('FieldResult', [ RelationInfo = namedtuple('RelationInfo', [ 'model_field', - 'related', + 'related_model', 'to_many', 'has_through_model' ]) @@ -77,7 +77,7 @@ def get_field_info(model): for field in [field for field in opts.fields if field.serialize and field.rel]: forward_relations[field.name] = RelationInfo( model_field=field, - related=_resolve_model(field.rel.to), + related_model=_resolve_model(field.rel.to), to_many=False, has_through_model=False ) @@ -86,7 +86,7 @@ def get_field_info(model): for field in [field for field in opts.many_to_many if field.serialize]: forward_relations[field.name] = RelationInfo( model_field=field, - related=_resolve_model(field.rel.to), + related_model=_resolve_model(field.rel.to), to_many=True, has_through_model=( not field.rel.through._meta.auto_created @@ -99,7 +99,7 @@ def get_field_info(model): accessor_name = relation.get_accessor_name() reverse_relations[accessor_name] = RelationInfo( model_field=None, - related=relation.model, + related_model=relation.model, to_many=relation.field.rel.multiple, has_through_model=False ) @@ -109,7 +109,7 @@ def get_field_info(model): accessor_name = relation.get_accessor_name() reverse_relations[accessor_name] = RelationInfo( model_field=None, - related=relation.model, + related_model=relation.model, to_many=True, has_through_model=( (getattr(relation.field.rel, 'through', None) is not None) -- cgit v1.2.3 From 77e3021fea3e30382b9770eac25371495e0b156b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sat, 20 Dec 2014 16:26:51 +0000 Subject: Better behaviour with null and '' for blank HTML fields. --- rest_framework/fields.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/fields.py b/rest_framework/fields.py index c40dc3fb..aab80982 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -273,7 +273,11 @@ class Field(object): return empty return self.default_empty_html ret = dictionary[self.field_name] - return self.default_empty_html if (ret == '') else ret + if ret == '' and self.allow_null: + # If the field is blank, and null is a valid value then + # determine if we should use null instead. + return '' if getattr(self, 'allow_blank', False) else None + return ret return dictionary.get(self.field_name, empty) def get_attribute(self, instance): @@ -545,8 +549,6 @@ class CharField(Field): 'min_length': _('Ensure this field has at least {min_length} characters.') } initial = '' - coerce_blank_to_null = False - default_empty_html = '' def __init__(self, **kwargs): self.allow_blank = kwargs.pop('allow_blank', False) @@ -560,11 +562,6 @@ class CharField(Field): message = self.error_messages['min_length'].format(min_length=min_length) self.validators.append(MinLengthValidator(min_length, message=message)) - if self.allow_null and (not self.allow_blank) and (self.default is empty): - # HTML input cannot represent `None` values, so we need to - # forcibly coerce empty HTML values to `None` if `allow_null=True`. - self.default_empty_html = None - def run_validation(self, data=empty): # Test for the empty string here so that it does not get validated, # and so that subclasses do not need to handle it explicitly -- cgit v1.2.3 From 03c4eb11305dcc9f366cdd008a5985bcf47c13ce Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sat, 20 Dec 2014 16:32:07 +0000 Subject: Use custom ListSerializer for pagination if one is specified on the serializer. --- rest_framework/pagination.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index fb451285..f46b0dfa 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -68,7 +68,12 @@ class BasePaginationSerializer(serializers.Serializer): except AttributeError: object_serializer = DefaultObjectSerializer - self.fields[results_field] = serializers.ListSerializer( + try: + list_serializer_class = object_serializer.Meta.list_serializer_class + except AttributeError: + list_serializer_class = serializers.ListSerializer + + self.fields[results_field] = list_serializer_class( child=object_serializer(), source='object_list' ) -- cgit v1.2.3 From 6c5ff712783ae7e6edebb52508f1d43249f1aa00 Mon Sep 17 00:00:00 2001 From: Remi Paulmier Date: Mon, 22 Dec 2014 18:05:07 +0100 Subject: fix the way to use textarea rather than input with models.TextField --- rest_framework/utils/field_mapping.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index fca97b4b..b16e9df0 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -80,7 +80,7 @@ def get_field_kwargs(field_name, model_field): kwargs['decimal_places'] = decimal_places if isinstance(model_field, models.TextField): - kwargs['style'] = {'type': 'textarea'} + kwargs['style'] = {'base_template': 'textarea.html'} if isinstance(model_field, models.AutoField) or not model_field.editable: # If this field is read-only, then return early. -- cgit v1.2.3 From 399cb165b0ba26df6052c114eb77961dc578e686 Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Tue, 23 Dec 2014 12:11:45 -0800 Subject: Remove commented code (warning during compression) manage.py compress —force causes a warning here.--- rest_framework/templates/rest_framework/vertical/list_fieldset.html | 4 ---- 1 file changed, 4 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/templates/rest_framework/vertical/list_fieldset.html b/rest_framework/templates/rest_framework/vertical/list_fieldset.html index 1d86c7f2..82d7b5f4 100644 --- a/rest_framework/templates/rest_framework/vertical/list_fieldset.html +++ b/rest_framework/templates/rest_framework/vertical/list_fieldset.html @@ -1,8 +1,4 @@
{% if field.label %}{{ field.label }}{% endif %} -

Lists are not currently supported in HTML input.

-- cgit v1.2.3 From 35768344db45b9fa6bd94c3fd48d5e232027434e Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Tue, 23 Dec 2014 12:12:22 -0800 Subject: Remove ‘/‘ from inside variable block {{ }} manage.py compress —force causes a warning here.--- rest_framework/templates/rest_framework/inline/checkbox_multiple.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/templates/rest_framework/inline/checkbox_multiple.html b/rest_framework/templates/rest_framework/inline/checkbox_multiple.html index 6caf6440..09349686 100644 --- a/rest_framework/templates/rest_framework/inline/checkbox_multiple.html +++ b/rest_framework/templates/rest_framework/inline/checkbox_multiple.html @@ -5,7 +5,7 @@ {% for key, text in field.choices.items %}
-- cgit v1.2.3 From c2e00a075cb4b44c644ad5d62f2be0fd19e62c5f Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 26 Dec 2014 15:25:13 +0000 Subject: Paginated serializers should get context. --- rest_framework/pagination.py | 1 + 1 file changed, 1 insertion(+) (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index f46b0dfa..f31e5fa4 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -77,6 +77,7 @@ class BasePaginationSerializer(serializers.Serializer): child=object_serializer(), source='object_list' ) + self.fields[results_field].bind(field_name=results_field, parent=self) class PaginationSerializer(BasePaginationSerializer): -- cgit v1.2.3 From ef2eff2abac64ffbed621bb9a72a2229841a1db1 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 28 Dec 2014 11:07:38 +0000 Subject: Only pass max_length for CharField. Closes #2317. --- rest_framework/utils/field_mapping.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index b16e9df0..b2f4dd80 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -106,7 +106,7 @@ def get_field_kwargs(field_name, model_field): # Ensure that max_length is passed explicitly as a keyword arg, # rather than as a validator. max_length = getattr(model_field, 'max_length', None) - if max_length is not None: + if max_length is not None and isinstance(model_field, models.CharField): kwargs['max_length'] = max_length validator_kwarg = [ validator for validator in validator_kwarg -- cgit v1.2.3 From efa5942ce1c5d2286fd91994b52fb73a5690426c Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 28 Dec 2014 12:02:52 +0000 Subject: Support namespaced router URLs with DefaultRouter. --- rest_framework/compat.py | 10 ++++++++++ rest_framework/routers.py | 5 ++++- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 69fdd793..ba26a3cd 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -50,6 +50,16 @@ except ImportError: from django.http import HttpResponse as HttpResponseBase +# request only provides `resolver_match` from 1.5 onwards. +def get_resolver_match(request): + try: + return request.resolver_match + except AttributeError: + # Django < 1.5 + from django.core.urlresolvers import resolve + return resolve(request.path_info) + + # django-filter is optional try: import django_filters diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 1cb65b1c..61f3ccab 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -21,7 +21,7 @@ from django.conf.urls import patterns, url from django.core.exceptions import ImproperlyConfigured from django.core.urlresolvers import NoReverseMatch from rest_framework import views -from rest_framework.compat import OrderedDict +from rest_framework.compat import get_resolver_match, OrderedDict from rest_framework.response import Response from rest_framework.reverse import reverse from rest_framework.urlpatterns import format_suffix_patterns @@ -292,7 +292,10 @@ class DefaultRouter(SimpleRouter): def get(self, request, *args, **kwargs): ret = OrderedDict() + namespace = get_resolver_match(request).namespace for key, url_name in api_root_dict.items(): + if namespace: + url_name = namespace + ':' + url_name try: ret[key] = reverse( url_name, -- cgit v1.2.3 From 5d8c45681a945b955d9336b0fd1e4ebccf0df895 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sun, 28 Dec 2014 18:48:42 +0000 Subject: Update copryright for 2015. Closes #2247. --- rest_framework/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 6808b74b..dec89b3e 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -11,7 +11,7 @@ __title__ = 'Django REST framework' __version__ = '3.0.2' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' -__copyright__ = 'Copyright 2011-2014 Tom Christie' +__copyright__ = 'Copyright 2011-2015 Tom Christie' # Version synonym VERSION = __version__ -- cgit v1.2.3 From a7479721c844926f377085d8c336a2f60b7b2a38 Mon Sep 17 00:00:00 2001 From: Kyle Valade Date: Mon, 29 Dec 2014 00:35:00 -0800 Subject: First pass at refactoring get_field_info in utils.model_meta --- rest_framework/utils/model_meta.py | 57 ++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 14 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/utils/model_meta.py b/rest_framework/utils/model_meta.py index c98725c6..375d2e8c 100644 --- a/rest_framework/utils/model_meta.py +++ b/rest_framework/utils/model_meta.py @@ -35,7 +35,7 @@ def _resolve_model(obj): Resolve supplied `obj` to a Django model class. `obj` must be a Django model class itself, or a string - representation of one. Useful in situtations like GH #1225 where + representation of one. Useful in situations like GH #1225 where Django may not have resolved a string-based reference to a model in another model's foreign key definition. @@ -56,23 +56,44 @@ def _resolve_model(obj): def get_field_info(model): """ - Given a model class, returns a `FieldInfo` instance containing metadata - about the various field types on the model. + Given a model class, returns a `FieldInfo` instance, which is a + `namedtuple`, containing metadata about the various field types on the model + including information about their relationships. """ opts = model._meta.concrete_model._meta - # Deal with the primary key. + pk = _get_pk(opts) + fields = _get_fields(opts) + forward_relations = _get_forward_relationships(opts) + reverse_relations = _get_reverse_relationships(opts) + fields_and_pk = _merge_fields_and_pk(pk, fields) + relationships = _merge_relationships(forward_relations, reverse_relations) + + return FieldInfo(pk, fields, forward_relations, reverse_relations, + fields_and_pk, relationships) + + +def _get_pk(opts): pk = opts.pk while pk.rel and pk.rel.parent_link: - # If model is a child via multitable inheritance, use parent's pk. + # If model is a child via multi-table inheritance, use parent's pk. pk = pk.rel.to._meta.pk - # Deal with regular fields. + return pk + + +def _get_fields(opts): fields = OrderedDict() for field in [field for field in opts.fields if field.serialize and not field.rel]: fields[field.name] = field - # Deal with forward relationships. + return fields + + +def _get_forward_relationships(opts): + """ + Returns an `OrderedDict` of field names to `RelationInfo`. + """ forward_relations = OrderedDict() for field in [field for field in opts.fields if field.serialize and field.rel]: forward_relations[field.name] = RelationInfo( @@ -93,7 +114,13 @@ def get_field_info(model): ) ) - # Deal with reverse relationships. + return forward_relations + + +def _get_reverse_relationships(opts): + """ + Returns an `OrderedDict` of field names to `RelationInfo`. + """ reverse_relations = OrderedDict() for relation in opts.get_all_related_objects(): accessor_name = relation.get_accessor_name() @@ -117,18 +144,20 @@ def get_field_info(model): ) ) - # Shortcut that merges both regular fields and the pk, - # for simplifying regular field lookup. + return reverse_relations + + +def _merge_fields_and_pk(pk, fields): fields_and_pk = OrderedDict() fields_and_pk['pk'] = pk fields_and_pk[pk.name] = pk fields_and_pk.update(fields) - # Shortcut that merges both forward and reverse relationships + return fields_and_pk - relations = OrderedDict( + +def _merge_relationships(forward_relations, reverse_relations): + return OrderedDict( list(forward_relations.items()) + list(reverse_relations.items()) ) - - return FieldInfo(pk, fields, forward_relations, reverse_relations, fields_and_pk, relations) -- cgit v1.2.3 From 0b8a83bd624673cb0a05e01c691729ccee3a8782 Mon Sep 17 00:00:00 2001 From: Craig Blaszczyk Date: Sun, 28 Dec 2014 18:20:41 +0000 Subject: update internationalisation instructions to prevent symlinking; add base .po file --- rest_framework/locale/en_US/LC_MESSAGES/django.po | 277 ++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 rest_framework/locale/en_US/LC_MESSAGES/django.po (limited to 'rest_framework') diff --git a/rest_framework/locale/en_US/LC_MESSAGES/django.po b/rest_framework/locale/en_US/LC_MESSAGES/django.po new file mode 100644 index 00000000..510ce0aa --- /dev/null +++ b/rest_framework/locale/en_US/LC_MESSAGES/django.po @@ -0,0 +1,277 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-12-28 17:49+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: rest_framework/authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "" + +#: rest_framework/authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "" + +#: rest_framework/authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"" +msgstr "" + +#: rest_framework/exceptions.py:39 +msgid "A server error occured" +msgstr "" + +#: rest_framework/exceptions.py:74 +msgid "Malformed request." +msgstr "" + +#: rest_framework/exceptions.py:79 +msgid "Incorrect authentication credentials." +msgstr "" + +#: rest_framework/exceptions.py:84 +msgid "Authentication credentials were not provided." +msgstr "" + +#: rest_framework/exceptions.py:89 +msgid "You do not have permission to perform this action." +msgstr "" + +#: rest_framework/exceptions.py:94 +#, python-format +msgid "Method '%s' not allowed." +msgstr "" + +#: rest_framework/exceptions.py:105 +msgid "Could not satisfy the request Accept header" +msgstr "" + +#: rest_framework/exceptions.py:117 +#, python-format +msgid "Unsupported media type '%s' in request." +msgstr "" + +#: rest_framework/exceptions.py:128 +msgid "Request was throttled." +msgstr "" + +#: rest_framework/exceptions.py:130 +#, python-format +msgid "Expected available in %(wait)d second." +msgid_plural "Expected available in %(wait)d seconds." +msgstr[0] "" +msgstr[1] "" + +#: rest_framework/fields.py:152 rest_framework/relations.py:131 +#: rest_framework/relations.py:155 rest_framework/validators.py:77 +#: rest_framework/validators.py:155 +msgid "This field is required." +msgstr "" + +#: rest_framework/fields.py:153 +msgid "This field may not be null." +msgstr "" + +#: rest_framework/fields.py:484 rest_framework/fields.py:512 +msgid "`{input}` is not a valid boolean." +msgstr "" + +#: rest_framework/fields.py:547 +msgid "This field may not be blank." +msgstr "" + +#: rest_framework/fields.py:548 rest_framework/fields.py:1250 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "" + +#: rest_framework/fields.py:549 +msgid "Ensure this field has at least {min_length} characters." +msgstr "" + +#: rest_framework/fields.py:584 +msgid "Enter a valid email address." +msgstr "" + +#: rest_framework/fields.py:601 +msgid "This value does not match the required pattern." +msgstr "" + +#: rest_framework/fields.py:612 +msgid "" +"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." +msgstr "" + +#: rest_framework/fields.py:624 +msgid "Enter a valid URL." +msgstr "" + +#: rest_framework/fields.py:637 +msgid "A valid integer is required." +msgstr "" + +#: rest_framework/fields.py:638 rest_framework/fields.py:672 +#: rest_framework/fields.py:705 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "" + +#: rest_framework/fields.py:639 rest_framework/fields.py:673 +#: rest_framework/fields.py:706 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "" + +#: rest_framework/fields.py:640 rest_framework/fields.py:674 +#: rest_framework/fields.py:710 +msgid "String value too large" +msgstr "" + +#: rest_framework/fields.py:671 rest_framework/fields.py:704 +msgid "A valid number is required." +msgstr "" + +#: rest_framework/fields.py:707 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "" + +#: rest_framework/fields.py:708 +msgid "Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "" + +#: rest_framework/fields.py:709 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "" + +#: rest_framework/fields.py:793 +msgid "Datetime has wrong format. Use one of these formats instead: {format}" +msgstr "" + +#: rest_framework/fields.py:794 +msgid "Expected a datetime but got a date." +msgstr "" + +#: rest_framework/fields.py:858 +msgid "Date has wrong format. Use one of these formats instead: {format}" +msgstr "" + +#: rest_framework/fields.py:859 +msgid "Expected a date but got a datetime." +msgstr "" + +#: rest_framework/fields.py:916 +msgid "Time has wrong format. Use one of these formats instead: {format}" +msgstr "" + +#: rest_framework/fields.py:972 rest_framework/fields.py:1016 +msgid "`{input}` is not a valid choice." +msgstr "" + +#: rest_framework/fields.py:1017 rest_framework/serializers.py:474 +msgid "Expected a list of items but got type `{input_type}`." +msgstr "" + +#: rest_framework/fields.py:1047 +msgid "No file was submitted." +msgstr "" + +#: rest_framework/fields.py:1048 +msgid "The submitted data was not a file. Check the encoding type on the form." +msgstr "" + +#: rest_framework/fields.py:1049 +msgid "No filename could be determined." +msgstr "" + +#: rest_framework/fields.py:1050 +msgid "The submitted file is empty." +msgstr "" + +#: rest_framework/fields.py:1051 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "" + +#: rest_framework/fields.py:1093 +msgid "Upload a valid image. The file you uploaded was either not an " +msgstr "" + +#: rest_framework/fields.py:1119 +msgid "Expected a list of items but got type `{input_type}`" +msgstr "" + +#: rest_framework/generics.py:122 +msgid "Page is not 'last', nor can it be converted to an int." +msgstr "" + +#: rest_framework/generics.py:126 +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "" + +#: rest_framework/relations.py:132 +msgid "Invalid pk '{pk_value}' - object does not exist." +msgstr "" + +#: rest_framework/relations.py:133 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "" + +#: rest_framework/relations.py:156 +msgid "Invalid hyperlink - No URL match" +msgstr "" + +#: rest_framework/relations.py:157 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "" + +#: rest_framework/relations.py:158 +msgid "Invalid hyperlink - Object does not exist." +msgstr "" + +#: rest_framework/relations.py:159 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "" + +#: rest_framework/relations.py:294 +msgid "Object with {slug_name}={value} does not exist." +msgstr "" + +#: rest_framework/relations.py:295 +msgid "Invalid value." +msgstr "" + +#: rest_framework/serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "" + +#: rest_framework/validators.py:22 +msgid "This field must be unique." +msgstr "" + +#: rest_framework/validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "" + +#: rest_framework/validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "" + +#: rest_framework/validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "" + +#: rest_framework/validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "" -- cgit v1.2.3 From faf76a4b75f12f3fa9de4e3ec455daa239af4d89 Mon Sep 17 00:00:00 2001 From: Craig Blaszczyk Date: Wed, 31 Dec 2014 12:49:20 +0000 Subject: fix spelling & grammar errors --- rest_framework/exceptions.py | 2 +- rest_framework/generics.py | 2 +- rest_framework/locale/en_US/LC_MESSAGES/django.po | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py index bcfd8961..2586fc33 100644 --- a/rest_framework/exceptions.py +++ b/rest_framework/exceptions.py @@ -36,7 +36,7 @@ class APIException(Exception): Subclasses should provide `.status_code` and `.default_detail` properties. """ status_code = status.HTTP_500_INTERNAL_SERVER_ERROR - default_detail = _('A server error occured') + default_detail = _('A server error occurred') def __init__(self, detail=None): if detail is not None: diff --git a/rest_framework/generics.py b/rest_framework/generics.py index e6db155e..bdbc19a7 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -119,7 +119,7 @@ class GenericAPIView(views.APIView): if page == 'last': page_number = paginator.num_pages else: - raise Http404(_("Page is not 'last', nor can it be converted to an int.")) + raise Http404(_("Page is not 'last', and cannot be converted to an int.")) try: page = paginator.page(page_number) except InvalidPage as exc: diff --git a/rest_framework/locale/en_US/LC_MESSAGES/django.po b/rest_framework/locale/en_US/LC_MESSAGES/django.po index 510ce0aa..3bed9143 100644 --- a/rest_framework/locale/en_US/LC_MESSAGES/django.po +++ b/rest_framework/locale/en_US/LC_MESSAGES/django.po @@ -2,13 +2,13 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. -# +# #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-12-28 17:49+0000\n" +"POT-Creation-Date: 2014-12-31 12:48+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -30,7 +30,7 @@ msgid "Must include \"username\" and \"password\"" msgstr "" #: rest_framework/exceptions.py:39 -msgid "A server error occured" +msgid "A server error occurred" msgstr "" #: rest_framework/exceptions.py:74 @@ -212,7 +212,7 @@ msgid "Expected a list of items but got type `{input_type}`" msgstr "" #: rest_framework/generics.py:122 -msgid "Page is not 'last', nor can it be converted to an int." +msgid "Page is not 'last', and cannot be converted to an int." msgstr "" #: rest_framework/generics.py:126 -- cgit v1.2.3 From a90ba2bc11de5fb391b95d4fce84f87ae7f88eff Mon Sep 17 00:00:00 2001 From: Craig Blaszczyk Date: Wed, 31 Dec 2014 13:03:16 +0000 Subject: update error messages for language and consistency --- rest_framework/exceptions.py | 4 +-- rest_framework/fields.py | 17 ++++++------ rest_framework/generics.py | 2 +- rest_framework/locale/en_US/LC_MESSAGES/django.po | 33 ++++++++++++----------- 4 files changed, 28 insertions(+), 28 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py index 2586fc33..d78b7e97 100644 --- a/rest_framework/exceptions.py +++ b/rest_framework/exceptions.py @@ -36,7 +36,7 @@ class APIException(Exception): Subclasses should provide `.status_code` and `.default_detail` properties. """ status_code = status.HTTP_500_INTERNAL_SERVER_ERROR - default_detail = _('A server error occurred') + default_detail = _('A server error occurred.') def __init__(self, detail=None): if detail is not None: @@ -107,7 +107,7 @@ class MethodNotAllowed(APIException): class NotAcceptable(APIException): status_code = status.HTTP_406_NOT_ACCEPTABLE - default_detail = _('Could not satisfy the request Accept header') + default_detail = _('Could not satisfy the request Accept header.') def __init__(self, detail=None, available_renderers=None): if detail is not None: diff --git a/rest_framework/fields.py b/rest_framework/fields.py index c40dc3fb..0ff2b073 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -640,7 +640,7 @@ class IntegerField(Field): 'invalid': _('A valid integer is required.'), 'max_value': _('Ensure this value is less than or equal to {max_value}.'), 'min_value': _('Ensure this value is greater than or equal to {min_value}.'), - 'max_string_length': _('String value too large') + 'max_string_length': _('String value too large.') } MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs. @@ -674,7 +674,7 @@ class FloatField(Field): 'invalid': _("A valid number is required."), 'max_value': _('Ensure this value is less than or equal to {max_value}.'), 'min_value': _('Ensure this value is greater than or equal to {min_value}.'), - 'max_string_length': _('String value too large') + 'max_string_length': _('String value too large.') } MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs. @@ -710,7 +710,7 @@ class DecimalField(Field): 'max_digits': _('Ensure that there are no more than {max_digits} digits in total.'), 'max_decimal_places': _('Ensure that there are no more than {max_decimal_places} decimal places.'), 'max_whole_digits': _('Ensure that there are no more than {max_whole_digits} digits before the decimal point.'), - 'max_string_length': _('String value too large') + 'max_string_length': _('String value too large.') } MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs. @@ -793,7 +793,7 @@ class DecimalField(Field): class DateTimeField(Field): default_error_messages = { - 'invalid': _('Datetime has wrong format. Use one of these formats instead: {format}'), + 'invalid': _('Datetime has wrong format. Use one of these formats instead: {format}.'), 'date': _('Expected a datetime but got a date.'), } format = api_settings.DATETIME_FORMAT @@ -858,7 +858,7 @@ class DateTimeField(Field): class DateField(Field): default_error_messages = { - 'invalid': _('Date has wrong format. Use one of these formats instead: {format}'), + 'invalid': _('Date has wrong format. Use one of these formats instead: {format}.'), 'datetime': _('Expected a date but got a datetime.'), } format = api_settings.DATE_FORMAT @@ -916,7 +916,7 @@ class DateField(Field): class TimeField(Field): default_error_messages = { - 'invalid': _('Time has wrong format. Use one of these formats instead: {format}'), + 'invalid': _('Time has wrong format. Use one of these formats instead: {format}.'), } format = api_settings.TIME_FORMAT input_formats = api_settings.TIME_INPUT_FORMATS @@ -1093,8 +1093,7 @@ class FileField(Field): class ImageField(FileField): default_error_messages = { 'invalid_image': _( - 'Upload a valid image. The file you uploaded was either not an ' - 'image or a corrupted image.' + 'Upload a valid image. The file you uploaded was either not an image or a corrupted image.' ), } @@ -1119,7 +1118,7 @@ class ListField(Field): child = None initial = [] default_error_messages = { - 'not_a_list': _('Expected a list of items but got type `{input_type}`') + 'not_a_list': _('Expected a list of items but got type `{input_type}`.') } def __init__(self, *args, **kwargs): diff --git a/rest_framework/generics.py b/rest_framework/generics.py index bdbc19a7..680992d7 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -119,7 +119,7 @@ class GenericAPIView(views.APIView): if page == 'last': page_number = paginator.num_pages else: - raise Http404(_("Page is not 'last', and cannot be converted to an int.")) + raise Http404(_("Choose a valid page number. Page numbers must be a whole number, or must be the string 'last'.")) try: page = paginator.page(page_number) except InvalidPage as exc: diff --git a/rest_framework/locale/en_US/LC_MESSAGES/django.po b/rest_framework/locale/en_US/LC_MESSAGES/django.po index 3bed9143..18f5fe18 100644 --- a/rest_framework/locale/en_US/LC_MESSAGES/django.po +++ b/rest_framework/locale/en_US/LC_MESSAGES/django.po @@ -2,13 +2,13 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. -# +# #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-12-31 12:48+0000\n" +"POT-Creation-Date: 2014-12-31 13:02+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -30,7 +30,7 @@ msgid "Must include \"username\" and \"password\"" msgstr "" #: rest_framework/exceptions.py:39 -msgid "A server error occurred" +msgid "A server error occurred." msgstr "" #: rest_framework/exceptions.py:74 @@ -55,7 +55,7 @@ msgid "Method '%s' not allowed." msgstr "" #: rest_framework/exceptions.py:105 -msgid "Could not satisfy the request Accept header" +msgid "Could not satisfy the request Accept header." msgstr "" #: rest_framework/exceptions.py:117 @@ -92,7 +92,7 @@ msgstr "" msgid "This field may not be blank." msgstr "" -#: rest_framework/fields.py:548 rest_framework/fields.py:1250 +#: rest_framework/fields.py:548 rest_framework/fields.py:1249 msgid "Ensure this field has no more than {max_length} characters." msgstr "" @@ -133,7 +133,7 @@ msgstr "" #: rest_framework/fields.py:640 rest_framework/fields.py:674 #: rest_framework/fields.py:710 -msgid "String value too large" +msgid "String value too large." msgstr "" #: rest_framework/fields.py:671 rest_framework/fields.py:704 @@ -155,7 +155,7 @@ msgid "" msgstr "" #: rest_framework/fields.py:793 -msgid "Datetime has wrong format. Use one of these formats instead: {format}" +msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" #: rest_framework/fields.py:794 @@ -163,7 +163,7 @@ msgid "Expected a datetime but got a date." msgstr "" #: rest_framework/fields.py:858 -msgid "Date has wrong format. Use one of these formats instead: {format}" +msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" #: rest_framework/fields.py:859 @@ -171,14 +171,15 @@ msgid "Expected a date but got a datetime." msgstr "" #: rest_framework/fields.py:916 -msgid "Time has wrong format. Use one of these formats instead: {format}" +msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" #: rest_framework/fields.py:972 rest_framework/fields.py:1016 msgid "`{input}` is not a valid choice." msgstr "" -#: rest_framework/fields.py:1017 rest_framework/serializers.py:474 +#: rest_framework/fields.py:1017 rest_framework/fields.py:1118 +#: rest_framework/serializers.py:474 msgid "Expected a list of items but got type `{input_type}`." msgstr "" @@ -204,15 +205,15 @@ msgid "" msgstr "" #: rest_framework/fields.py:1093 -msgid "Upload a valid image. The file you uploaded was either not an " -msgstr "" - -#: rest_framework/fields.py:1119 -msgid "Expected a list of items but got type `{input_type}`" +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." msgstr "" #: rest_framework/generics.py:122 -msgid "Page is not 'last', and cannot be converted to an int." +msgid "" +"Choose a valid page number. Page numbers must be a whole number, or must be " +"the string 'last'." msgstr "" #: rest_framework/generics.py:126 -- cgit v1.2.3 From 6fb37207d18949031fb7203d6fd67ee503df0a34 Mon Sep 17 00:00:00 2001 From: Craig Blaszczyk Date: Fri, 2 Jan 2015 11:11:13 +0000 Subject: add missing period; update generated translations --- rest_framework/exceptions.py | 2 +- rest_framework/locale/en_US/LC_MESSAGES/django.po | 96 ++++++++++++++--------- 2 files changed, 59 insertions(+), 39 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py index d78b7e97..c8cedfce 100644 --- a/rest_framework/exceptions.py +++ b/rest_framework/exceptions.py @@ -91,7 +91,7 @@ class PermissionDenied(APIException): class NotFound(APIException): status_code = status.HTTP_404_NOT_FOUND - default_detail = _('Not found') + default_detail = _('Not found.') class MethodNotAllowed(APIException): diff --git a/rest_framework/locale/en_US/LC_MESSAGES/django.po b/rest_framework/locale/en_US/LC_MESSAGES/django.po index 18f5fe18..56902073 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: 2014-12-31 13:02+0000\n" +"POT-Creation-Date: 2015-01-02 11:10+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -50,24 +50,28 @@ msgid "You do not have permission to perform this action." msgstr "" #: rest_framework/exceptions.py:94 +msgid "Not found." +msgstr "" + +#: rest_framework/exceptions.py:99 #, python-format msgid "Method '%s' not allowed." msgstr "" -#: rest_framework/exceptions.py:105 +#: rest_framework/exceptions.py:110 msgid "Could not satisfy the request Accept header." msgstr "" -#: rest_framework/exceptions.py:117 +#: rest_framework/exceptions.py:122 #, python-format msgid "Unsupported media type '%s' in request." msgstr "" -#: rest_framework/exceptions.py:128 +#: rest_framework/exceptions.py:133 msgid "Request was throttled." msgstr "" -#: rest_framework/exceptions.py:130 +#: rest_framework/exceptions.py:135 #, python-format msgid "Expected available in %(wait)d second." msgid_plural "Expected available in %(wait)d seconds." @@ -84,127 +88,127 @@ msgstr "" msgid "This field may not be null." msgstr "" -#: rest_framework/fields.py:484 rest_framework/fields.py:512 +#: rest_framework/fields.py:480 rest_framework/fields.py:508 msgid "`{input}` is not a valid boolean." msgstr "" -#: rest_framework/fields.py:547 +#: rest_framework/fields.py:543 msgid "This field may not be blank." msgstr "" -#: rest_framework/fields.py:548 rest_framework/fields.py:1249 +#: rest_framework/fields.py:544 rest_framework/fields.py:1252 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: rest_framework/fields.py:549 +#: rest_framework/fields.py:545 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: rest_framework/fields.py:584 +#: rest_framework/fields.py:587 msgid "Enter a valid email address." msgstr "" -#: rest_framework/fields.py:601 +#: rest_framework/fields.py:604 msgid "This value does not match the required pattern." msgstr "" -#: rest_framework/fields.py:612 +#: rest_framework/fields.py:615 msgid "" "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." msgstr "" -#: rest_framework/fields.py:624 +#: rest_framework/fields.py:627 msgid "Enter a valid URL." msgstr "" -#: rest_framework/fields.py:637 +#: rest_framework/fields.py:640 msgid "A valid integer is required." msgstr "" -#: rest_framework/fields.py:638 rest_framework/fields.py:672 -#: rest_framework/fields.py:705 +#: rest_framework/fields.py:641 rest_framework/fields.py:675 +#: rest_framework/fields.py:708 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: rest_framework/fields.py:639 rest_framework/fields.py:673 -#: rest_framework/fields.py:706 +#: rest_framework/fields.py:642 rest_framework/fields.py:676 +#: rest_framework/fields.py:709 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: rest_framework/fields.py:640 rest_framework/fields.py:674 -#: rest_framework/fields.py:710 +#: rest_framework/fields.py:643 rest_framework/fields.py:677 +#: rest_framework/fields.py:713 msgid "String value too large." msgstr "" -#: rest_framework/fields.py:671 rest_framework/fields.py:704 +#: rest_framework/fields.py:674 rest_framework/fields.py:707 msgid "A valid number is required." msgstr "" -#: rest_framework/fields.py:707 +#: rest_framework/fields.py:710 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: rest_framework/fields.py:708 +#: rest_framework/fields.py:711 msgid "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: rest_framework/fields.py:709 +#: rest_framework/fields.py:712 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: rest_framework/fields.py:793 +#: rest_framework/fields.py:796 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: rest_framework/fields.py:794 +#: rest_framework/fields.py:797 msgid "Expected a datetime but got a date." msgstr "" -#: rest_framework/fields.py:858 +#: rest_framework/fields.py:861 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: rest_framework/fields.py:859 +#: rest_framework/fields.py:862 msgid "Expected a date but got a datetime." msgstr "" -#: rest_framework/fields.py:916 +#: rest_framework/fields.py:919 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: rest_framework/fields.py:972 rest_framework/fields.py:1016 +#: rest_framework/fields.py:975 rest_framework/fields.py:1019 msgid "`{input}` is not a valid choice." msgstr "" -#: rest_framework/fields.py:1017 rest_framework/fields.py:1118 -#: rest_framework/serializers.py:474 +#: rest_framework/fields.py:1020 rest_framework/fields.py:1121 +#: rest_framework/serializers.py:476 msgid "Expected a list of items but got type `{input_type}`." msgstr "" -#: rest_framework/fields.py:1047 +#: rest_framework/fields.py:1050 msgid "No file was submitted." msgstr "" -#: rest_framework/fields.py:1048 +#: rest_framework/fields.py:1051 msgid "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: rest_framework/fields.py:1049 +#: rest_framework/fields.py:1052 msgid "No filename could be determined." msgstr "" -#: rest_framework/fields.py:1050 +#: rest_framework/fields.py:1053 msgid "The submitted file is empty." msgstr "" -#: rest_framework/fields.py:1051 +#: rest_framework/fields.py:1054 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: rest_framework/fields.py:1093 +#: rest_framework/fields.py:1096 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." @@ -276,3 +280,19 @@ msgstr "" #: rest_framework/validators.py:247 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" + +#: rest_framework/versioning.py:39 +msgid "Invalid version in 'Accept' header." +msgstr "" + +#: rest_framework/versioning.py:70 rest_framework/versioning.py:112 +msgid "Invalid version in URL path." +msgstr "" + +#: rest_framework/versioning.py:138 +msgid "Invalid version in hostname." +msgstr "" + +#: rest_framework/versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "" -- cgit v1.2.3 From 8cf37449715c32c4a692667814466c7f32e8734f Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 5 Jan 2015 10:52:18 +0000 Subject: Ensure no invalid min_length/min_value/max_value arguments. Closes #2369. --- rest_framework/utils/field_mapping.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index b2f4dd80..cba40d31 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -10,6 +10,11 @@ from rest_framework.validators import UniqueValidator import inspect +NUMERIC_FIELD_TYPES = ( + models.IntegerField, models.FloatField, models.DecimalField +) + + class ClassLookupDict(object): """ Takes a dictionary with classes as keys. @@ -119,7 +124,7 @@ def get_field_kwargs(field_name, model_field): validator.limit_value for validator in validator_kwarg if isinstance(validator, validators.MinLengthValidator) ), None) - if min_length is not None: + if min_length is not None and isinstance(model_field, models.CharField): kwargs['min_length'] = min_length validator_kwarg = [ validator for validator in validator_kwarg @@ -132,7 +137,7 @@ def get_field_kwargs(field_name, model_field): validator.limit_value for validator in validator_kwarg if isinstance(validator, validators.MaxValueValidator) ), None) - if max_value is not None: + if max_value is not None and isinstance(model_field, NUMERIC_FIELD_TYPES): kwargs['max_value'] = max_value validator_kwarg = [ validator for validator in validator_kwarg @@ -145,7 +150,7 @@ def get_field_kwargs(field_name, model_field): validator.limit_value for validator in validator_kwarg if isinstance(validator, validators.MinValueValidator) ), None) - if min_value is not None: + if min_value is not None and isinstance(model_field, NUMERIC_FIELD_TYPES): kwargs['min_value'] = min_value validator_kwarg = [ validator for validator in validator_kwarg -- cgit v1.2.3 From b6ca7248ebcf95a95e1911aa0b130f653b8bf690 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 5 Jan 2015 14:32:12 +0000 Subject: required=False allows omission of value for output. Closes #2342 --- rest_framework/fields.py | 2 ++ rest_framework/serializers.py | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/fields.py b/rest_framework/fields.py index aab80982..cc9410aa 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -288,6 +288,8 @@ class Field(object): try: return get_attribute(instance, self.source_attrs) except (KeyError, AttributeError) as exc: + if not self.required and self.default is empty: + raise SkipField() msg = ( 'Got {exc_type} when attempting to get a value for field ' '`{field}` on serializer `{serializer}`.\nThe serializer ' diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 6f89df0d..53f092d7 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -419,8 +419,14 @@ class Serializer(BaseSerializer): fields = [field for field in self.fields.values() if not field.write_only] for field in fields: - attribute = field.get_attribute(instance) + try: + attribute = field.get_attribute(instance) + except SkipField: + continue + if attribute is None: + # We skip `to_representation` for `None` values so that + # fields do not have to explicitly deal with that case. ret[field.field_name] = None else: ret[field.field_name] = field.to_representation(attribute) -- cgit v1.2.3 From 49dc037a961b618baf8eb189b094633238867b41 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 5 Jan 2015 15:03:09 +0000 Subject: Update docstring --- rest_framework/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 623ed586..08a58433 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -236,11 +236,11 @@ class BaseSerializer(Field): class SerializerMetaclass(type): """ - This metaclass sets a dictionary named `base_fields` on the class. + This metaclass sets a dictionary named `_declared_fields` on the class. Any instances of `Field` included as attributes on either the class or on any of its superclasses will be include in the - `base_fields` dictionary. + `_declared_fields` dictionary. """ @classmethod -- cgit v1.2.3 From 6fd33ddea9e5b8f9e979e573a27873131846ea48 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 5 Jan 2015 15:04:01 +0000 Subject: Udpate docstring --- rest_framework/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 53f092d7..e373cd10 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -236,11 +236,11 @@ class BaseSerializer(Field): class SerializerMetaclass(type): """ - This metaclass sets a dictionary named `base_fields` on the class. + This metaclass sets a dictionary named `_declared_fields` on the class. Any instances of `Field` included as attributes on either the class or on any of its superclasses will be include in the - `base_fields` dictionary. + `_declared_fields` dictionary. """ @classmethod -- cgit v1.2.3 From 26ac2656e5e0b3d01a67551910113a305d2a2820 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 5 Jan 2015 16:20:15 +0000 Subject: Pass init arguments through to serializer from pagination serializer. Closes #2355. Normally a serializer won't need these arguments on __init__, but if a user has customized __init__ they may expect them to be available. --- rest_framework/pagination.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index f31e5fa4..9c8dda8f 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -37,16 +37,13 @@ class PreviousPageField(serializers.Field): return replace_query_param(url, self.page_field, page) -class DefaultObjectSerializer(serializers.ReadOnlyField): +class DefaultObjectSerializer(serializers.Serializer): """ If no object serializer is specified, then this serializer will be applied as the default. """ - - def __init__(self, source=None, many=None, context=None): - # Note: Swallow context and many kwargs - only required for - # eg. ModelSerializer. - super(DefaultObjectSerializer, self).__init__(source=source) + def to_representation(self, value): + return value class BasePaginationSerializer(serializers.Serializer): @@ -74,10 +71,9 @@ class BasePaginationSerializer(serializers.Serializer): list_serializer_class = serializers.ListSerializer self.fields[results_field] = list_serializer_class( - child=object_serializer(), + child=object_serializer(*args, **kwargs), source='object_list' ) - self.fields[results_field].bind(field_name=results_field, parent=self) class PaginationSerializer(BasePaginationSerializer): -- cgit v1.2.3 From 07ad0474c0cef8f8e88d299eca9dffbe6d01c10d Mon Sep 17 00:00:00 2001 From: Ryan Gaffney Date: Tue, 6 Jan 2015 14:34:36 -0800 Subject: Fix compatibility comment regarding OrderedDict --- rest_framework/compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/compat.py b/rest_framework/compat.py index ba26a3cd..b1f6f2fa 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -36,7 +36,7 @@ def unicode_to_repr(value): # OrderedDict only available in Python 2.7. # This will always be the case in Django 1.7 and above, as these versions # no longer support Python 2.6. -# For Django <= 1.6 and Python 2.6 fall back to OrderedDict. +# For Django <= 1.6 and Python 2.6 fall back to SortedDict. try: from collections import OrderedDict except ImportError: -- cgit v1.2.3 From 9b4177b6ea38de6e86b0fe723834b6ef36af15b3 Mon Sep 17 00:00:00 2001 From: Craig Blaszczyk Date: Wed, 7 Jan 2015 11:41:06 +0000 Subject: switch to using format strings in error messages; raise NotFound when pagination fails to provide a more useful error message --- rest_framework/exceptions.py | 28 ++++++++++++++-------------- rest_framework/generics.py | 14 ++++++++------ 2 files changed, 22 insertions(+), 20 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py index c8cedfce..dfc57293 100644 --- a/rest_framework/exceptions.py +++ b/rest_framework/exceptions.py @@ -7,8 +7,7 @@ In addition Django's built in 403 and 404 exceptions are handled. from __future__ import unicode_literals from django.utils import six from django.utils.encoding import force_text -from django.utils.translation import ugettext_lazy as _ -from django.utils.translation import ungettext_lazy +from django.utils.translation import ugettext_lazy as _, ungettext from rest_framework import status import math @@ -96,13 +95,13 @@ class NotFound(APIException): class MethodNotAllowed(APIException): status_code = status.HTTP_405_METHOD_NOT_ALLOWED - default_detail = _("Method '%s' not allowed.") + default_detail = _("Method {method} not allowed.") def __init__(self, method, detail=None): if detail is not None: self.detail = force_text(detail) else: - self.detail = force_text(self.default_detail) % method + self.detail = force_text(self.default_detail).format(method=method) class NotAcceptable(APIException): @@ -119,23 +118,22 @@ class NotAcceptable(APIException): class UnsupportedMediaType(APIException): status_code = status.HTTP_415_UNSUPPORTED_MEDIA_TYPE - default_detail = _("Unsupported media type '%s' in request.") + default_detail = _("Unsupported media type '{media_type}' in request.") def __init__(self, media_type, detail=None): if detail is not None: self.detail = force_text(detail) else: - self.detail = force_text(self.default_detail) % media_type + self.detail = force_text(self.default_detail).format( + media_type=media_type + ) class Throttled(APIException): status_code = status.HTTP_429_TOO_MANY_REQUESTS default_detail = _('Request was throttled.') - extra_detail = ungettext_lazy( - 'Expected available in %(wait)d second.', - 'Expected available in %(wait)d seconds.', - 'wait' - ) + extra_detail_singular = 'Expected available in {wait} second.' + extra_detail_plural = 'Expected available in {wait} seconds.' def __init__(self, wait=None, detail=None): if detail is not None: @@ -147,6 +145,8 @@ class Throttled(APIException): self.wait = None else: self.wait = math.ceil(wait) - self.detail += ' ' + force_text( - self.extra_detail % {'wait': self.wait} - ) + self.detail += ' ' + force_text(ungettext( + self.extra_detail_singular.format(wait=self.wait), + self.extra_detail_plural.format(wait=self.wait), + self.wait + )) diff --git a/rest_framework/generics.py b/rest_framework/generics.py index 680992d7..fe92355d 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -10,6 +10,7 @@ from django.shortcuts import get_object_or_404 as _get_object_or_404 from django.utils import six from django.utils.translation import ugettext as _ from rest_framework import views, mixins +from rest_framework.exceptions import NotFound from rest_framework.settings import api_settings @@ -119,15 +120,16 @@ class GenericAPIView(views.APIView): if page == 'last': page_number = paginator.num_pages else: - raise Http404(_("Choose a valid page number. Page numbers must be a whole number, or must be the string 'last'.")) + raise NotFound(_("Choose a valid page number. Page numbers must be a whole number, or must be the string 'last'.")) + + page_number = -1 try: page = paginator.page(page_number) except InvalidPage as exc: - error_format = _('Invalid page (%(page_number)s): %(message)s') - raise Http404(error_format % { - 'page_number': page_number, - 'message': six.text_type(exc) - }) + error_format = _('Invalid page ({page_number}): {message}') + raise NotFound(error_format.format( + page_number=page_number, message=six.text_type(exc) + )) return page -- cgit v1.2.3 From 3819ae35ac70ef25804f285b7b59edf2f67ea915 Mon Sep 17 00:00:00 2001 From: Craig Blaszczyk Date: Wed, 7 Jan 2015 11:42:36 +0000 Subject: recompile pofile with new python format strings --- rest_framework/locale/en_US/LC_MESSAGES/django.po | 153 ++++++++++------------ 1 file changed, 69 insertions(+), 84 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/locale/en_US/LC_MESSAGES/django.po b/rest_framework/locale/en_US/LC_MESSAGES/django.po index 56902073..7c5a6c02 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: 2015-01-02 11:10+0000\n" +"POT-Creation-Date: 2015-01-07 11:40+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,282 +17,267 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: rest_framework/authtoken/serializers.py:20 +#: authtoken/serializers.py:20 msgid "User account is disabled." msgstr "" -#: rest_framework/authtoken/serializers.py:23 +#: authtoken/serializers.py:23 msgid "Unable to log in with provided credentials." msgstr "" -#: rest_framework/authtoken/serializers.py:26 +#: authtoken/serializers.py:26 msgid "Must include \"username\" and \"password\"" msgstr "" -#: rest_framework/exceptions.py:39 +#: exceptions.py:38 msgid "A server error occurred." msgstr "" -#: rest_framework/exceptions.py:74 +#: exceptions.py:73 msgid "Malformed request." msgstr "" -#: rest_framework/exceptions.py:79 +#: exceptions.py:78 msgid "Incorrect authentication credentials." msgstr "" -#: rest_framework/exceptions.py:84 +#: exceptions.py:83 msgid "Authentication credentials were not provided." msgstr "" -#: rest_framework/exceptions.py:89 +#: exceptions.py:88 msgid "You do not have permission to perform this action." msgstr "" -#: rest_framework/exceptions.py:94 +#: exceptions.py:93 msgid "Not found." msgstr "" -#: rest_framework/exceptions.py:99 -#, python-format -msgid "Method '%s' not allowed." +#: exceptions.py:98 +msgid "Method {method} not allowed." msgstr "" -#: rest_framework/exceptions.py:110 +#: exceptions.py:109 msgid "Could not satisfy the request Accept header." msgstr "" -#: rest_framework/exceptions.py:122 -#, python-format -msgid "Unsupported media type '%s' in request." +#: exceptions.py:121 +msgid "Unsupported media type '{media_type}' in request." msgstr "" -#: rest_framework/exceptions.py:133 +#: exceptions.py:134 msgid "Request was throttled." msgstr "" -#: rest_framework/exceptions.py:135 -#, python-format -msgid "Expected available in %(wait)d second." -msgid_plural "Expected available in %(wait)d seconds." -msgstr[0] "" -msgstr[1] "" - -#: rest_framework/fields.py:152 rest_framework/relations.py:131 -#: rest_framework/relations.py:155 rest_framework/validators.py:77 -#: rest_framework/validators.py:155 +#: fields.py:152 relations.py:131 relations.py:155 validators.py:77 +#: validators.py:155 msgid "This field is required." msgstr "" -#: rest_framework/fields.py:153 +#: fields.py:153 msgid "This field may not be null." msgstr "" -#: rest_framework/fields.py:480 rest_framework/fields.py:508 +#: fields.py:480 fields.py:508 msgid "`{input}` is not a valid boolean." msgstr "" -#: rest_framework/fields.py:543 +#: fields.py:543 msgid "This field may not be blank." msgstr "" -#: rest_framework/fields.py:544 rest_framework/fields.py:1252 +#: fields.py:544 fields.py:1252 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: rest_framework/fields.py:545 +#: fields.py:545 msgid "Ensure this field has at least {min_length} characters." msgstr "" -#: rest_framework/fields.py:587 +#: fields.py:587 msgid "Enter a valid email address." msgstr "" -#: rest_framework/fields.py:604 +#: fields.py:604 msgid "This value does not match the required pattern." msgstr "" -#: rest_framework/fields.py:615 +#: fields.py:615 msgid "" "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." msgstr "" -#: rest_framework/fields.py:627 +#: fields.py:627 msgid "Enter a valid URL." msgstr "" -#: rest_framework/fields.py:640 +#: fields.py:640 msgid "A valid integer is required." msgstr "" -#: rest_framework/fields.py:641 rest_framework/fields.py:675 -#: rest_framework/fields.py:708 +#: fields.py:641 fields.py:675 fields.py:708 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: rest_framework/fields.py:642 rest_framework/fields.py:676 -#: rest_framework/fields.py:709 +#: fields.py:642 fields.py:676 fields.py:709 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: rest_framework/fields.py:643 rest_framework/fields.py:677 -#: rest_framework/fields.py:713 +#: fields.py:643 fields.py:677 fields.py:713 msgid "String value too large." msgstr "" -#: rest_framework/fields.py:674 rest_framework/fields.py:707 +#: fields.py:674 fields.py:707 msgid "A valid number is required." msgstr "" -#: rest_framework/fields.py:710 +#: fields.py:710 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: rest_framework/fields.py:711 +#: fields.py:711 msgid "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: rest_framework/fields.py:712 +#: fields.py:712 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: rest_framework/fields.py:796 +#: fields.py:796 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: rest_framework/fields.py:797 +#: fields.py:797 msgid "Expected a datetime but got a date." msgstr "" -#: rest_framework/fields.py:861 +#: fields.py:861 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: rest_framework/fields.py:862 +#: fields.py:862 msgid "Expected a date but got a datetime." msgstr "" -#: rest_framework/fields.py:919 +#: fields.py:919 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: rest_framework/fields.py:975 rest_framework/fields.py:1019 +#: fields.py:975 fields.py:1019 msgid "`{input}` is not a valid choice." msgstr "" -#: rest_framework/fields.py:1020 rest_framework/fields.py:1121 -#: rest_framework/serializers.py:476 +#: fields.py:1020 fields.py:1121 serializers.py:476 msgid "Expected a list of items but got type `{input_type}`." msgstr "" -#: rest_framework/fields.py:1050 +#: fields.py:1050 msgid "No file was submitted." msgstr "" -#: rest_framework/fields.py:1051 +#: fields.py:1051 msgid "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: rest_framework/fields.py:1052 +#: fields.py:1052 msgid "No filename could be determined." msgstr "" -#: rest_framework/fields.py:1053 +#: fields.py:1053 msgid "The submitted file is empty." msgstr "" -#: rest_framework/fields.py:1054 +#: fields.py:1054 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: rest_framework/fields.py:1096 +#: fields.py:1096 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: rest_framework/generics.py:122 +#: generics.py:123 msgid "" "Choose a valid page number. Page numbers must be a whole number, or must be " "the string 'last'." msgstr "" -#: rest_framework/generics.py:126 -#, python-format -msgid "Invalid page (%(page_number)s): %(message)s" +#: generics.py:129 +msgid "Invalid page ({page_number}): {message}" msgstr "" -#: rest_framework/relations.py:132 +#: relations.py:132 msgid "Invalid pk '{pk_value}' - object does not exist." msgstr "" -#: rest_framework/relations.py:133 +#: relations.py:133 msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" -#: rest_framework/relations.py:156 +#: relations.py:156 msgid "Invalid hyperlink - No URL match" msgstr "" -#: rest_framework/relations.py:157 +#: relations.py:157 msgid "Invalid hyperlink - Incorrect URL match." msgstr "" -#: rest_framework/relations.py:158 +#: relations.py:158 msgid "Invalid hyperlink - Object does not exist." msgstr "" -#: rest_framework/relations.py:159 +#: relations.py:159 msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: rest_framework/relations.py:294 +#: relations.py:294 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: rest_framework/relations.py:295 +#: relations.py:295 msgid "Invalid value." msgstr "" -#: rest_framework/serializers.py:299 +#: serializers.py:299 msgid "Invalid data. Expected a dictionary, but got {datatype}." msgstr "" -#: rest_framework/validators.py:22 +#: validators.py:22 msgid "This field must be unique." msgstr "" -#: rest_framework/validators.py:76 +#: validators.py:76 msgid "The fields {field_names} must make a unique set." msgstr "" -#: rest_framework/validators.py:219 +#: validators.py:219 msgid "This field must be unique for the \"{date_field}\" date." msgstr "" -#: rest_framework/validators.py:234 +#: validators.py:234 msgid "This field must be unique for the \"{date_field}\" month." msgstr "" -#: rest_framework/validators.py:247 +#: validators.py:247 msgid "This field must be unique for the \"{date_field}\" year." msgstr "" -#: rest_framework/versioning.py:39 +#: versioning.py:39 msgid "Invalid version in 'Accept' header." msgstr "" -#: rest_framework/versioning.py:70 rest_framework/versioning.py:112 +#: versioning.py:70 versioning.py:112 msgid "Invalid version in URL path." msgstr "" -#: rest_framework/versioning.py:138 +#: versioning.py:138 msgid "Invalid version in hostname." msgstr "" -#: rest_framework/versioning.py:160 +#: versioning.py:160 msgid "Invalid version in query parameter." msgstr "" -- cgit v1.2.3 From fe5d93c8cbc5f3a9b1b6715208c70f485be68bdf Mon Sep 17 00:00:00 2001 From: Craig Blaszczyk Date: Wed, 7 Jan 2015 11:44:18 +0000 Subject: remove hardcoded page number --- rest_framework/generics.py | 1 - 1 file changed, 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/generics.py b/rest_framework/generics.py index fe92355d..7c4d5e95 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -122,7 +122,6 @@ class GenericAPIView(views.APIView): else: raise NotFound(_("Choose a valid page number. Page numbers must be a whole number, or must be the string 'last'.")) - page_number = -1 try: page = paginator.page(page_number) except InvalidPage as exc: -- cgit v1.2.3 From 4c32083b8b59a50877633910055313dad7bb117e Mon Sep 17 00:00:00 2001 From: Craig Blaszczyk Date: Wed, 7 Jan 2015 12:01:11 +0000 Subject: use double quotes for user visible strings; end user visible strings in full stops; add some missing translation tags --- rest_framework/authentication.py | 17 +++++---- rest_framework/authtoken/serializers.py | 6 +-- rest_framework/exceptions.py | 24 ++++++------ rest_framework/fields.py | 68 ++++++++++++++++----------------- rest_framework/generics.py | 2 +- rest_framework/relations.py | 16 ++++---- rest_framework/serializers.py | 4 +- rest_framework/validators.py | 14 +++---- rest_framework/versioning.py | 8 ++-- 9 files changed, 80 insertions(+), 79 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index 124ef68a..7e86a7b9 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import base64 from django.contrib.auth import authenticate from django.middleware.csrf import CsrfViewMiddleware +from django.utils.translation import ugettext_lazy as _ from rest_framework import exceptions, HTTP_HEADER_ENCODING from rest_framework.authtoken.models import Token @@ -65,16 +66,16 @@ class BasicAuthentication(BaseAuthentication): return None if len(auth) == 1: - msg = 'Invalid basic header. No credentials provided.' + msg = _("Invalid basic header. No credentials provided.") raise exceptions.AuthenticationFailed(msg) elif len(auth) > 2: - msg = 'Invalid basic header. Credentials string should not contain spaces.' + msg = _("Invalid basic header. Credentials string should not contain spaces.") raise exceptions.AuthenticationFailed(msg) try: auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':') except (TypeError, UnicodeDecodeError): - msg = 'Invalid basic header. Credentials not correctly base64 encoded' + msg = _("Invalid basic header. Credentials not correctly base64 encoded.") raise exceptions.AuthenticationFailed(msg) userid, password = auth_parts[0], auth_parts[2] @@ -86,7 +87,7 @@ class BasicAuthentication(BaseAuthentication): """ user = authenticate(username=userid, password=password) if user is None or not user.is_active: - raise exceptions.AuthenticationFailed('Invalid username/password') + raise exceptions.AuthenticationFailed(_("Invalid username/password.")) return (user, None) def authenticate_header(self, request): @@ -152,10 +153,10 @@ class TokenAuthentication(BaseAuthentication): return None if len(auth) == 1: - msg = 'Invalid token header. No credentials provided.' + msg = _("Invalid token header. No credentials provided.") raise exceptions.AuthenticationFailed(msg) elif len(auth) > 2: - msg = 'Invalid token header. Token string should not contain spaces.' + msg = _("Invalid token header. Token string should not contain spaces.") raise exceptions.AuthenticationFailed(msg) return self.authenticate_credentials(auth[1]) @@ -164,10 +165,10 @@ class TokenAuthentication(BaseAuthentication): try: token = self.model.objects.get(key=key) except self.model.DoesNotExist: - raise exceptions.AuthenticationFailed('Invalid token') + raise exceptions.AuthenticationFailed(_("Invalid token")) if not token.user.is_active: - raise exceptions.AuthenticationFailed('User inactive or deleted') + raise exceptions.AuthenticationFailed(_("User inactive or deleted")) return (token.user, token) diff --git a/rest_framework/authtoken/serializers.py b/rest_framework/authtoken/serializers.py index f31dded1..78fe6a11 100644 --- a/rest_framework/authtoken/serializers.py +++ b/rest_framework/authtoken/serializers.py @@ -17,13 +17,13 @@ class AuthTokenSerializer(serializers.Serializer): if user: if not user.is_active: - msg = _('User account is disabled.') + msg = _("User account is disabled.") raise exceptions.ValidationError(msg) else: - msg = _('Unable to log in with provided credentials.') + msg = _("Unable to log in with provided credentials.") raise exceptions.ValidationError(msg) else: - msg = _('Must include "username" and "password"') + msg = _("Must include \"username\" and \"password\"") raise exceptions.ValidationError(msg) attrs['user'] = user diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py index dfc57293..3ca8e538 100644 --- a/rest_framework/exceptions.py +++ b/rest_framework/exceptions.py @@ -35,7 +35,7 @@ class APIException(Exception): Subclasses should provide `.status_code` and `.default_detail` properties. """ status_code = status.HTTP_500_INTERNAL_SERVER_ERROR - default_detail = _('A server error occurred.') + default_detail = _("A server error occurred.") def __init__(self, detail=None): if detail is not None: @@ -52,7 +52,7 @@ class APIException(Exception): # built in `ValidationError`. For example: # # from rest_framework import serializers -# raise serializers.ValidationError('Value was invalid') +# raise serializers.ValidationError("Value was invalid") class ValidationError(APIException): status_code = status.HTTP_400_BAD_REQUEST @@ -70,32 +70,32 @@ class ValidationError(APIException): class ParseError(APIException): status_code = status.HTTP_400_BAD_REQUEST - default_detail = _('Malformed request.') + default_detail = _("Malformed request.") class AuthenticationFailed(APIException): status_code = status.HTTP_401_UNAUTHORIZED - default_detail = _('Incorrect authentication credentials.') + default_detail = _("Incorrect authentication credentials.") class NotAuthenticated(APIException): status_code = status.HTTP_401_UNAUTHORIZED - default_detail = _('Authentication credentials were not provided.') + default_detail = _("Authentication credentials were not provided.") class PermissionDenied(APIException): status_code = status.HTTP_403_FORBIDDEN - default_detail = _('You do not have permission to perform this action.') + default_detail = _("You do not have permission to perform this action.") class NotFound(APIException): status_code = status.HTTP_404_NOT_FOUND - default_detail = _('Not found.') + default_detail = _("Not found.") class MethodNotAllowed(APIException): status_code = status.HTTP_405_METHOD_NOT_ALLOWED - default_detail = _("Method {method} not allowed.") + default_detail = _("Method '{method}' not allowed.") def __init__(self, method, detail=None): if detail is not None: @@ -106,7 +106,7 @@ class MethodNotAllowed(APIException): class NotAcceptable(APIException): status_code = status.HTTP_406_NOT_ACCEPTABLE - default_detail = _('Could not satisfy the request Accept header.') + default_detail = _("Could not satisfy the request Accept header.") def __init__(self, detail=None, available_renderers=None): if detail is not None: @@ -131,9 +131,9 @@ class UnsupportedMediaType(APIException): class Throttled(APIException): status_code = status.HTTP_429_TOO_MANY_REQUESTS - default_detail = _('Request was throttled.') - extra_detail_singular = 'Expected available in {wait} second.' - extra_detail_plural = 'Expected available in {wait} seconds.' + default_detail = _("Request was throttled.") + extra_detail_singular = "Expected available in {wait} second." + extra_detail_plural = "Expected available in {wait} seconds." def __init__(self, wait=None, detail=None): if detail is not None: diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 0ff2b073..8a781b35 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -149,8 +149,8 @@ class Field(object): _creation_counter = 0 default_error_messages = { - 'required': _('This field is required.'), - 'null': _('This field may not be null.') + 'required': _("This field is required."), + 'null': _("This field may not be null.") } default_validators = [] default_empty_html = empty @@ -477,7 +477,7 @@ class Field(object): class BooleanField(Field): default_error_messages = { - 'invalid': _('`{input}` is not a valid boolean.') + 'invalid': _("`{input}` is not a valid boolean.") } default_empty_html = False initial = False @@ -505,7 +505,7 @@ class BooleanField(Field): class NullBooleanField(Field): default_error_messages = { - 'invalid': _('`{input}` is not a valid boolean.') + 'invalid': _("`{input}` is not a valid boolean.") } initial = None TRUE_VALUES = set(('t', 'T', 'true', 'True', 'TRUE', '1', 1, True)) @@ -540,9 +540,9 @@ class NullBooleanField(Field): class CharField(Field): default_error_messages = { - '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.') + '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.") } initial = '' coerce_blank_to_null = False @@ -584,7 +584,7 @@ class CharField(Field): class EmailField(CharField): default_error_messages = { - 'invalid': _('Enter a valid email address.') + 'invalid': _("Enter a valid email address.") } def __init__(self, **kwargs): @@ -601,7 +601,7 @@ class EmailField(CharField): class RegexField(CharField): default_error_messages = { - 'invalid': _('This value does not match the required pattern.') + 'invalid': _("This value does not match the required pattern.") } def __init__(self, regex, **kwargs): @@ -637,10 +637,10 @@ class URLField(CharField): class IntegerField(Field): default_error_messages = { - 'invalid': _('A valid integer is required.'), - 'max_value': _('Ensure this value is less than or equal to {max_value}.'), - 'min_value': _('Ensure this value is greater than or equal to {min_value}.'), - 'max_string_length': _('String value too large.') + 'invalid': _("A valid integer is required."), + 'max_value': _("Ensure this value is less than or equal to {max_value}."), + 'min_value': _("Ensure this value is greater than or equal to {min_value}."), + 'max_string_length': _("String value too large.") } MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs. @@ -672,9 +672,9 @@ class IntegerField(Field): class FloatField(Field): default_error_messages = { 'invalid': _("A valid number is required."), - 'max_value': _('Ensure this value is less than or equal to {max_value}.'), - 'min_value': _('Ensure this value is greater than or equal to {min_value}.'), - 'max_string_length': _('String value too large.') + 'max_value': _("Ensure this value is less than or equal to {max_value}."), + 'min_value': _("Ensure this value is greater than or equal to {min_value}."), + 'max_string_length': _("String value too large.") } MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs. @@ -704,13 +704,13 @@ class FloatField(Field): class DecimalField(Field): default_error_messages = { - 'invalid': _('A valid number is required.'), - 'max_value': _('Ensure this value is less than or equal to {max_value}.'), - 'min_value': _('Ensure this value is greater than or equal to {min_value}.'), - 'max_digits': _('Ensure that there are no more than {max_digits} digits in total.'), - 'max_decimal_places': _('Ensure that there are no more than {max_decimal_places} decimal places.'), - 'max_whole_digits': _('Ensure that there are no more than {max_whole_digits} digits before the decimal point.'), - 'max_string_length': _('String value too large.') + 'invalid': _("A valid number is required."), + 'max_value': _("Ensure this value is less than or equal to {max_value}."), + 'min_value': _("Ensure this value is greater than or equal to {min_value}."), + 'max_digits': _("Ensure that there are no more than {max_digits} digits in total."), + 'max_decimal_places': _("Ensure that there are no more than {max_decimal_places} decimal places."), + 'max_whole_digits': _("Ensure that there are no more than {max_whole_digits} digits before the decimal point."), + 'max_string_length': _("String value too large.") } MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs. @@ -793,8 +793,8 @@ class DecimalField(Field): class DateTimeField(Field): default_error_messages = { - 'invalid': _('Datetime has wrong format. Use one of these formats instead: {format}.'), - 'date': _('Expected a datetime but got a date.'), + 'invalid': _("Datetime has wrong format. Use one of these formats instead: {format}."), + 'date': _("Expected a datetime but got a date."), } format = api_settings.DATETIME_FORMAT input_formats = api_settings.DATETIME_INPUT_FORMATS @@ -858,8 +858,8 @@ class DateTimeField(Field): class DateField(Field): default_error_messages = { - 'invalid': _('Date has wrong format. Use one of these formats instead: {format}.'), - 'datetime': _('Expected a date but got a datetime.'), + 'invalid': _("Date has wrong format. Use one of these formats instead: {format}."), + 'datetime': _("Expected a date but got a datetime."), } format = api_settings.DATE_FORMAT input_formats = api_settings.DATE_INPUT_FORMATS @@ -916,7 +916,7 @@ class DateField(Field): class TimeField(Field): default_error_messages = { - 'invalid': _('Time has wrong format. Use one of these formats instead: {format}.'), + 'invalid': _("Time has wrong format. Use one of these formats instead: {format}."), } format = api_settings.TIME_FORMAT input_formats = api_settings.TIME_INPUT_FORMATS @@ -972,7 +972,7 @@ class TimeField(Field): class ChoiceField(Field): default_error_messages = { - 'invalid_choice': _('`{input}` is not a valid choice.') + 'invalid_choice': _("`{input}` is not a valid choice.") } def __init__(self, choices, **kwargs): @@ -1016,8 +1016,8 @@ class ChoiceField(Field): class MultipleChoiceField(ChoiceField): default_error_messages = { - 'invalid_choice': _('`{input}` is not a valid choice.'), - 'not_a_list': _('Expected a list of items but got type `{input_type}`.') + 'invalid_choice': _("`{input}` is not a valid choice."), + 'not_a_list': _("Expected a list of items but got type `{input_type}`.") } default_empty_html = [] @@ -1051,7 +1051,7 @@ class FileField(Field): 'invalid': _("The submitted data was not a file. Check the encoding type on the form."), 'no_name': _("No filename could be determined."), 'empty': _("The submitted file is empty."), - 'max_length': _('Ensure this filename has at most {max_length} characters (it has {length}).'), + 'max_length': _("Ensure this filename has at most {max_length} characters (it has {length})."), } use_url = api_settings.UPLOADED_FILES_USE_URL @@ -1118,7 +1118,7 @@ class ListField(Field): child = None initial = [] default_error_messages = { - 'not_a_list': _('Expected a list of items but got type `{input_type}`.') + 'not_a_list': _("Expected a list of items but got type `{input_type}`.") } def __init__(self, *args, **kwargs): @@ -1249,7 +1249,7 @@ class ModelField(Field): that do not have a serializer field to be mapped to. """ default_error_messages = { - 'max_length': _('Ensure this field has no more than {max_length} characters.'), + 'max_length': _("Ensure this field has no more than {max_length} characters."), } def __init__(self, model_field, **kwargs): diff --git a/rest_framework/generics.py b/rest_framework/generics.py index 7c4d5e95..c7053d8f 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -125,7 +125,7 @@ class GenericAPIView(views.APIView): try: page = paginator.page(page_number) except InvalidPage as exc: - error_format = _('Invalid page ({page_number}): {message}') + error_format = _("Invalid page ({page_number}): {message}.") raise NotFound(error_format.format( page_number=page_number, message=six.text_type(exc) )) diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 7b119291..3737b21f 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -128,9 +128,9 @@ class StringRelatedField(RelatedField): class PrimaryKeyRelatedField(RelatedField): default_error_messages = { - 'required': _('This field is required.'), + 'required': _("This field is required."), 'does_not_exist': _("Invalid pk '{pk_value}' - object does not exist."), - 'incorrect_type': _('Incorrect type. Expected pk value, received {data_type}.'), + 'incorrect_type': _("Incorrect type. Expected pk value, received {data_type}."), } def use_pk_only_optimization(self): @@ -152,11 +152,11 @@ class HyperlinkedRelatedField(RelatedField): lookup_field = 'pk' default_error_messages = { - 'required': _('This field is required.'), - 'no_match': _('Invalid hyperlink - No URL match'), - 'incorrect_match': _('Invalid hyperlink - Incorrect URL match.'), - 'does_not_exist': _('Invalid hyperlink - Object does not exist.'), - 'incorrect_type': _('Incorrect type. Expected URL string, received {data_type}.'), + 'required': _("This field is required."), + 'no_match': _("Invalid hyperlink - No URL match."), + 'incorrect_match': _("Invalid hyperlink - Incorrect URL match."), + 'does_not_exist': _("Invalid hyperlink - Object does not exist."), + 'incorrect_type': _("Incorrect type. Expected URL string, received {data_type}."), } def __init__(self, view_name=None, **kwargs): @@ -292,7 +292,7 @@ class SlugRelatedField(RelatedField): default_error_messages = { 'does_not_exist': _("Object with {slug_name}={value} does not exist."), - 'invalid': _('Invalid value.'), + 'invalid': _("Invalid value."), } def __init__(self, slug_field=None, **kwargs): diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 623ed586..9d7c8884 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -296,7 +296,7 @@ def get_validation_error_detail(exc): @six.add_metaclass(SerializerMetaclass) class Serializer(BaseSerializer): default_error_messages = { - 'invalid': _('Invalid data. Expected a dictionary, but got {datatype}.') + 'invalid': _("Invalid data. Expected a dictionary, but got {datatype}.") } @property @@ -473,7 +473,7 @@ class ListSerializer(BaseSerializer): many = True default_error_messages = { - 'not_a_list': _('Expected a list of items but got type `{input_type}`.') + 'not_a_list': _("Expected a list of items but got type `{input_type}`.") } def __init__(self, *args, **kwargs): diff --git a/rest_framework/validators.py b/rest_framework/validators.py index e3719b8d..cf6f0718 100644 --- a/rest_framework/validators.py +++ b/rest_framework/validators.py @@ -19,7 +19,7 @@ class UniqueValidator: Should be applied to an individual field on the serializer. """ - message = _('This field must be unique.') + message = _("This field must be unique.") def __init__(self, queryset, message=None): self.queryset = queryset @@ -73,8 +73,8 @@ class UniqueTogetherValidator: Should be applied to the serializer class, not to an individual field. """ - message = _('The fields {field_names} must make a unique set.') - missing_message = _('This field is required.') + message = _("The fields {field_names} must make a unique set.") + missing_message = _("This field is required.") def __init__(self, queryset, fields, message=None): self.queryset = queryset @@ -152,7 +152,7 @@ class UniqueTogetherValidator: class BaseUniqueForValidator: message = None - missing_message = _('This field is required.') + missing_message = _("This field is required.") def __init__(self, queryset, field, date_field, message=None): self.queryset = queryset @@ -216,7 +216,7 @@ class BaseUniqueForValidator: class UniqueForDateValidator(BaseUniqueForValidator): - message = _('This field must be unique for the "{date_field}" date.') + message = _("This field must be unique for the \"{date_field}\" date.") def filter_queryset(self, attrs, queryset): value = attrs[self.field] @@ -231,7 +231,7 @@ class UniqueForDateValidator(BaseUniqueForValidator): class UniqueForMonthValidator(BaseUniqueForValidator): - message = _('This field must be unique for the "{date_field}" month.') + message = _("This field must be unique for the \"{date_field}\" month.") def filter_queryset(self, attrs, queryset): value = attrs[self.field] @@ -244,7 +244,7 @@ class UniqueForMonthValidator(BaseUniqueForValidator): class UniqueForYearValidator(BaseUniqueForValidator): - message = _('This field must be unique for the "{date_field}" year.') + message = _("This field must be unique for the \"{date_field}\" year.") def filter_queryset(self, attrs, queryset): value = attrs[self.field] diff --git a/rest_framework/versioning.py b/rest_framework/versioning.py index 440efd13..587ba9f1 100644 --- a/rest_framework/versioning.py +++ b/rest_framework/versioning.py @@ -67,7 +67,7 @@ class URLPathVersioning(BaseVersioning): Host: example.com Accept: application/json """ - invalid_version_message = _('Invalid version in URL path.') + invalid_version_message = _("Invalid version in URL path.") def determine_version(self, request, *args, **kwargs): version = kwargs.get(self.version_param, self.default_version) @@ -109,7 +109,7 @@ class NamespaceVersioning(BaseVersioning): Host: example.com Accept: application/json """ - invalid_version_message = _('Invalid version in URL path.') + invalid_version_message = _("Invalid version in URL path.") def determine_version(self, request, *args, **kwargs): resolver_match = getattr(request, 'resolver_match', None) @@ -135,7 +135,7 @@ class HostNameVersioning(BaseVersioning): Accept: application/json """ hostname_regex = re.compile(r'^([a-zA-Z0-9]+)\.[a-zA-Z0-9]+\.[a-zA-Z0-9]+$') - invalid_version_message = _('Invalid version in hostname.') + invalid_version_message = _("Invalid version in hostname.") def determine_version(self, request, *args, **kwargs): hostname, seperator, port = request.get_host().partition(':') @@ -157,7 +157,7 @@ class QueryParameterVersioning(BaseVersioning): Host: example.com Accept: application/json """ - invalid_version_message = _('Invalid version in query parameter.') + invalid_version_message = _("Invalid version in query parameter.") def determine_version(self, request, *args, **kwargs): version = request.query_params.get(self.version_param) -- cgit v1.2.3 From 662a907bdf821c29b42b60ce2b44eb8149a85bd7 Mon Sep 17 00:00:00 2001 From: Craig Blaszczyk Date: Wed, 7 Jan 2015 12:02:04 +0000 Subject: update source strings --- rest_framework/locale/en_US/LC_MESSAGES/django.po | 42 ++++++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/locale/en_US/LC_MESSAGES/django.po b/rest_framework/locale/en_US/LC_MESSAGES/django.po index 7c5a6c02..5d0d3a04 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: 2015-01-07 11:40+0000\n" +"POT-Creation-Date: 2015-01-07 11:58+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,6 +17,38 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "" + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "" + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "" + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "" + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "" + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "" + +#: authentication.py:168 +msgid "Invalid token" +msgstr "" + +#: authentication.py:171 +msgid "User inactive or deleted" +msgstr "" + #: authtoken/serializers.py:20 msgid "User account is disabled." msgstr "" @@ -54,7 +86,7 @@ msgid "Not found." msgstr "" #: exceptions.py:98 -msgid "Method {method} not allowed." +msgid "Method '{method}' not allowed." msgstr "" #: exceptions.py:109 @@ -206,8 +238,8 @@ msgid "" "the string 'last'." msgstr "" -#: generics.py:129 -msgid "Invalid page ({page_number}): {message}" +#: generics.py:128 +msgid "Invalid page ({page_number}): {message}." msgstr "" #: relations.py:132 @@ -219,7 +251,7 @@ msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" #: relations.py:156 -msgid "Invalid hyperlink - No URL match" +msgid "Invalid hyperlink - No URL match." msgstr "" #: relations.py:157 -- cgit v1.2.3 From 9a4267049ba37883e3e0c21b5d453b9551343b8d Mon Sep 17 00:00:00 2001 From: Craig Blaszczyk Date: Wed, 7 Jan 2015 12:33:37 +0000 Subject: use double quotes in user messages --- rest_framework/exceptions.py | 4 ++-- rest_framework/fields.py | 2 +- rest_framework/generics.py | 4 ++-- rest_framework/relations.py | 2 +- rest_framework/versioning.py | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py index 3ca8e538..f8a43871 100644 --- a/rest_framework/exceptions.py +++ b/rest_framework/exceptions.py @@ -95,7 +95,7 @@ class NotFound(APIException): class MethodNotAllowed(APIException): status_code = status.HTTP_405_METHOD_NOT_ALLOWED - default_detail = _("Method '{method}' not allowed.") + default_detail = _("Method \"{method}\" not allowed.") def __init__(self, method, detail=None): if detail is not None: @@ -118,7 +118,7 @@ class NotAcceptable(APIException): class UnsupportedMediaType(APIException): status_code = status.HTTP_415_UNSUPPORTED_MEDIA_TYPE - default_detail = _("Unsupported media type '{media_type}' in request.") + default_detail = _("Unsupported media type \"{media_type}\" in request.") def __init__(self, media_type, detail=None): if detail is not None: diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 8a781b35..27944608 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -612,7 +612,7 @@ class RegexField(CharField): class SlugField(CharField): default_error_messages = { - 'invalid': _("Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens.") + 'invalid': _("Enter a valid \"slug\" consisting of letters, numbers, underscores or hyphens.") } def __init__(self, **kwargs): diff --git a/rest_framework/generics.py b/rest_framework/generics.py index c7053d8f..738ba544 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -120,12 +120,12 @@ class GenericAPIView(views.APIView): if page == 'last': page_number = paginator.num_pages else: - raise NotFound(_("Choose a valid page number. Page numbers must be a whole number, or must be the string 'last'.")) + raise NotFound(_("Choose a valid page number. Page numbers must be a whole number, or must be the string \"last\".")) try: page = paginator.page(page_number) except InvalidPage as exc: - error_format = _("Invalid page ({page_number}): {message}.") + error_format = _("Invalid page \"{page_number}\": {message}.") raise NotFound(error_format.format( page_number=page_number, message=six.text_type(exc) )) diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 3737b21f..42b624e7 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -129,7 +129,7 @@ class StringRelatedField(RelatedField): class PrimaryKeyRelatedField(RelatedField): default_error_messages = { 'required': _("This field is required."), - 'does_not_exist': _("Invalid pk '{pk_value}' - object does not exist."), + 'does_not_exist': _("Invalid pk \"{pk_value}\" - object does not exist."), 'incorrect_type': _("Incorrect type. Expected pk value, received {data_type}."), } diff --git a/rest_framework/versioning.py b/rest_framework/versioning.py index 587ba9f1..819c32df 100644 --- a/rest_framework/versioning.py +++ b/rest_framework/versioning.py @@ -36,7 +36,7 @@ class AcceptHeaderVersioning(BaseVersioning): Host: example.com Accept: application/json; version=1.0 """ - invalid_version_message = _("Invalid version in 'Accept' header.") + invalid_version_message = _("Invalid version in \"Accept\" header.") def determine_version(self, request, *args, **kwargs): media_type = _MediaType(request.accepted_media_type) -- cgit v1.2.3 From 91e316f7810157474d6246cd0024bd7f7cc31ff7 Mon Sep 17 00:00:00 2001 From: Craig Blaszczyk Date: Wed, 7 Jan 2015 12:46:23 +0000 Subject: prefer single quotes in source and double quotes in user visible strings; add some missing full stops to user visible strings --- rest_framework/authentication.py | 16 ++--- rest_framework/authtoken/serializers.py | 6 +- rest_framework/exceptions.py | 24 +++---- rest_framework/fields.py | 82 +++++++++++------------ rest_framework/generics.py | 4 +- rest_framework/locale/en_US/LC_MESSAGES/django.po | 23 ++++--- rest_framework/relations.py | 20 +++--- rest_framework/serializers.py | 4 +- rest_framework/validators.py | 14 ++-- rest_framework/versioning.py | 10 +-- 10 files changed, 102 insertions(+), 101 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index 7e86a7b9..11db0585 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -66,16 +66,16 @@ class BasicAuthentication(BaseAuthentication): return None if len(auth) == 1: - msg = _("Invalid basic header. No credentials provided.") + msg = _('Invalid basic header. No credentials provided.') raise exceptions.AuthenticationFailed(msg) elif len(auth) > 2: - msg = _("Invalid basic header. Credentials string should not contain spaces.") + msg = _('Invalid basic header. Credentials string should not contain spaces.') raise exceptions.AuthenticationFailed(msg) try: auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':') except (TypeError, UnicodeDecodeError): - msg = _("Invalid basic header. Credentials not correctly base64 encoded.") + msg = _('Invalid basic header. Credentials not correctly base64 encoded.') raise exceptions.AuthenticationFailed(msg) userid, password = auth_parts[0], auth_parts[2] @@ -87,7 +87,7 @@ class BasicAuthentication(BaseAuthentication): """ user = authenticate(username=userid, password=password) if user is None or not user.is_active: - raise exceptions.AuthenticationFailed(_("Invalid username/password.")) + raise exceptions.AuthenticationFailed(_('Invalid username/password.')) return (user, None) def authenticate_header(self, request): @@ -153,10 +153,10 @@ class TokenAuthentication(BaseAuthentication): return None if len(auth) == 1: - msg = _("Invalid token header. No credentials provided.") + msg = _('Invalid token header. No credentials provided.') raise exceptions.AuthenticationFailed(msg) elif len(auth) > 2: - msg = _("Invalid token header. Token string should not contain spaces.") + msg = _('Invalid token header. Token string should not contain spaces.') raise exceptions.AuthenticationFailed(msg) return self.authenticate_credentials(auth[1]) @@ -165,10 +165,10 @@ class TokenAuthentication(BaseAuthentication): try: token = self.model.objects.get(key=key) except self.model.DoesNotExist: - raise exceptions.AuthenticationFailed(_("Invalid token")) + raise exceptions.AuthenticationFailed(_('Invalid token.')) if not token.user.is_active: - raise exceptions.AuthenticationFailed(_("User inactive or deleted")) + raise exceptions.AuthenticationFailed(_('User inactive or deleted.')) return (token.user, token) diff --git a/rest_framework/authtoken/serializers.py b/rest_framework/authtoken/serializers.py index 78fe6a11..37ade255 100644 --- a/rest_framework/authtoken/serializers.py +++ b/rest_framework/authtoken/serializers.py @@ -17,13 +17,13 @@ class AuthTokenSerializer(serializers.Serializer): if user: if not user.is_active: - msg = _("User account is disabled.") + msg = _('User account is disabled.') raise exceptions.ValidationError(msg) else: - msg = _("Unable to log in with provided credentials.") + msg = _('Unable to log in with provided credentials.') raise exceptions.ValidationError(msg) else: - msg = _("Must include \"username\" and \"password\"") + msg = _('Must include "username" and "password".') raise exceptions.ValidationError(msg) attrs['user'] = user diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py index f8a43871..f62c9fe3 100644 --- a/rest_framework/exceptions.py +++ b/rest_framework/exceptions.py @@ -35,7 +35,7 @@ class APIException(Exception): Subclasses should provide `.status_code` and `.default_detail` properties. """ status_code = status.HTTP_500_INTERNAL_SERVER_ERROR - default_detail = _("A server error occurred.") + default_detail = _('A server error occurred.') def __init__(self, detail=None): if detail is not None: @@ -70,32 +70,32 @@ class ValidationError(APIException): class ParseError(APIException): status_code = status.HTTP_400_BAD_REQUEST - default_detail = _("Malformed request.") + default_detail = _('Malformed request.') class AuthenticationFailed(APIException): status_code = status.HTTP_401_UNAUTHORIZED - default_detail = _("Incorrect authentication credentials.") + default_detail = _('Incorrect authentication credentials.') class NotAuthenticated(APIException): status_code = status.HTTP_401_UNAUTHORIZED - default_detail = _("Authentication credentials were not provided.") + default_detail = _('Authentication credentials were not provided.') class PermissionDenied(APIException): status_code = status.HTTP_403_FORBIDDEN - default_detail = _("You do not have permission to perform this action.") + default_detail = _('You do not have permission to perform this action.') class NotFound(APIException): status_code = status.HTTP_404_NOT_FOUND - default_detail = _("Not found.") + default_detail = _('Not found.') class MethodNotAllowed(APIException): status_code = status.HTTP_405_METHOD_NOT_ALLOWED - default_detail = _("Method \"{method}\" not allowed.") + default_detail = _('Method "{method}" not allowed.') def __init__(self, method, detail=None): if detail is not None: @@ -106,7 +106,7 @@ class MethodNotAllowed(APIException): class NotAcceptable(APIException): status_code = status.HTTP_406_NOT_ACCEPTABLE - default_detail = _("Could not satisfy the request Accept header.") + default_detail = _('Could not satisfy the request Accept header.') def __init__(self, detail=None, available_renderers=None): if detail is not None: @@ -118,7 +118,7 @@ class NotAcceptable(APIException): class UnsupportedMediaType(APIException): status_code = status.HTTP_415_UNSUPPORTED_MEDIA_TYPE - default_detail = _("Unsupported media type \"{media_type}\" in request.") + default_detail = _('Unsupported media type "{media_type}" in request.') def __init__(self, media_type, detail=None): if detail is not None: @@ -131,9 +131,9 @@ class UnsupportedMediaType(APIException): class Throttled(APIException): status_code = status.HTTP_429_TOO_MANY_REQUESTS - default_detail = _("Request was throttled.") - extra_detail_singular = "Expected available in {wait} second." - extra_detail_plural = "Expected available in {wait} seconds." + default_detail = _('Request was throttled.') + extra_detail_singular = 'Expected available in {wait} second.' + extra_detail_plural = 'Expected available in {wait} seconds.' def __init__(self, wait=None, detail=None): if detail is not None: diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 27944608..76101608 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -149,8 +149,8 @@ class Field(object): _creation_counter = 0 default_error_messages = { - 'required': _("This field is required."), - 'null': _("This field may not be null.") + 'required': _('This field is required.'), + 'null': _('This field may not be null.') } default_validators = [] default_empty_html = empty @@ -477,7 +477,7 @@ class Field(object): class BooleanField(Field): default_error_messages = { - 'invalid': _("`{input}` is not a valid boolean.") + 'invalid': _('`{input}` is not a valid boolean.') } default_empty_html = False initial = False @@ -505,7 +505,7 @@ class BooleanField(Field): class NullBooleanField(Field): default_error_messages = { - 'invalid': _("`{input}` is not a valid boolean.") + 'invalid': _('`{input}` is not a valid boolean.') } initial = None TRUE_VALUES = set(('t', 'T', 'true', 'True', 'TRUE', '1', 1, True)) @@ -540,9 +540,9 @@ class NullBooleanField(Field): class CharField(Field): default_error_messages = { - '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.") + '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.') } initial = '' coerce_blank_to_null = False @@ -584,7 +584,7 @@ class CharField(Field): class EmailField(CharField): default_error_messages = { - 'invalid': _("Enter a valid email address.") + 'invalid': _('Enter a valid email address.') } def __init__(self, **kwargs): @@ -601,7 +601,7 @@ class EmailField(CharField): class RegexField(CharField): default_error_messages = { - 'invalid': _("This value does not match the required pattern.") + 'invalid': _('This value does not match the required pattern.') } def __init__(self, regex, **kwargs): @@ -612,7 +612,7 @@ class RegexField(CharField): class SlugField(CharField): default_error_messages = { - 'invalid': _("Enter a valid \"slug\" consisting of letters, numbers, underscores or hyphens.") + 'invalid': _('Enter a valid "slug" consisting of letters, numbers, underscores or hyphens.') } def __init__(self, **kwargs): @@ -624,7 +624,7 @@ class SlugField(CharField): class URLField(CharField): default_error_messages = { - 'invalid': _("Enter a valid URL.") + 'invalid': _('Enter a valid URL.') } def __init__(self, **kwargs): @@ -637,10 +637,10 @@ class URLField(CharField): class IntegerField(Field): default_error_messages = { - 'invalid': _("A valid integer is required."), - 'max_value': _("Ensure this value is less than or equal to {max_value}."), - 'min_value': _("Ensure this value is greater than or equal to {min_value}."), - 'max_string_length': _("String value too large.") + 'invalid': _('A valid integer is required.'), + 'max_value': _('Ensure this value is less than or equal to {max_value}.'), + 'min_value': _('Ensure this value is greater than or equal to {min_value}.'), + 'max_string_length': _('String value too large.') } MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs. @@ -671,10 +671,10 @@ class IntegerField(Field): class FloatField(Field): default_error_messages = { - 'invalid': _("A valid number is required."), - 'max_value': _("Ensure this value is less than or equal to {max_value}."), - 'min_value': _("Ensure this value is greater than or equal to {min_value}."), - 'max_string_length': _("String value too large.") + 'invalid': _('A valid number is required.'), + 'max_value': _('Ensure this value is less than or equal to {max_value}.'), + 'min_value': _('Ensure this value is greater than or equal to {min_value}.'), + 'max_string_length': _('String value too large.') } MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs. @@ -704,13 +704,13 @@ class FloatField(Field): class DecimalField(Field): default_error_messages = { - 'invalid': _("A valid number is required."), - 'max_value': _("Ensure this value is less than or equal to {max_value}."), - 'min_value': _("Ensure this value is greater than or equal to {min_value}."), - 'max_digits': _("Ensure that there are no more than {max_digits} digits in total."), - 'max_decimal_places': _("Ensure that there are no more than {max_decimal_places} decimal places."), - 'max_whole_digits': _("Ensure that there are no more than {max_whole_digits} digits before the decimal point."), - 'max_string_length': _("String value too large.") + 'invalid': _('A valid number is required.'), + 'max_value': _('Ensure this value is less than or equal to {max_value}.'), + 'min_value': _('Ensure this value is greater than or equal to {min_value}.'), + 'max_digits': _('Ensure that there are no more than {max_digits} digits in total.'), + 'max_decimal_places': _('Ensure that there are no more than {max_decimal_places} decimal places.'), + 'max_whole_digits': _('Ensure that there are no more than {max_whole_digits} digits before the decimal point.'), + 'max_string_length': _('String value too large.') } MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs. @@ -793,8 +793,8 @@ class DecimalField(Field): class DateTimeField(Field): default_error_messages = { - 'invalid': _("Datetime has wrong format. Use one of these formats instead: {format}."), - 'date': _("Expected a datetime but got a date."), + 'invalid': _('Datetime has wrong format. Use one of these formats instead: {format}.'), + 'date': _('Expected a datetime but got a date.'), } format = api_settings.DATETIME_FORMAT input_formats = api_settings.DATETIME_INPUT_FORMATS @@ -858,8 +858,8 @@ class DateTimeField(Field): class DateField(Field): default_error_messages = { - 'invalid': _("Date has wrong format. Use one of these formats instead: {format}."), - 'datetime': _("Expected a date but got a datetime."), + 'invalid': _('Date has wrong format. Use one of these formats instead: {format}.'), + 'datetime': _('Expected a date but got a datetime.'), } format = api_settings.DATE_FORMAT input_formats = api_settings.DATE_INPUT_FORMATS @@ -916,7 +916,7 @@ class DateField(Field): class TimeField(Field): default_error_messages = { - 'invalid': _("Time has wrong format. Use one of these formats instead: {format}."), + 'invalid': _('Time has wrong format. Use one of these formats instead: {format}.'), } format = api_settings.TIME_FORMAT input_formats = api_settings.TIME_INPUT_FORMATS @@ -972,7 +972,7 @@ class TimeField(Field): class ChoiceField(Field): default_error_messages = { - 'invalid_choice': _("`{input}` is not a valid choice.") + 'invalid_choice': _('`{input}` is not a valid choice.') } def __init__(self, choices, **kwargs): @@ -1016,8 +1016,8 @@ class ChoiceField(Field): class MultipleChoiceField(ChoiceField): default_error_messages = { - 'invalid_choice': _("`{input}` is not a valid choice."), - 'not_a_list': _("Expected a list of items but got type `{input_type}`.") + 'invalid_choice': _('`{input}` is not a valid choice.'), + 'not_a_list': _('Expected a list of items but got type `{input_type}`.') } default_empty_html = [] @@ -1047,11 +1047,11 @@ class MultipleChoiceField(ChoiceField): class FileField(Field): default_error_messages = { - 'required': _("No file was submitted."), - 'invalid': _("The submitted data was not a file. Check the encoding type on the form."), - 'no_name': _("No filename could be determined."), - 'empty': _("The submitted file is empty."), - 'max_length': _("Ensure this filename has at most {max_length} characters (it has {length})."), + 'required': _('No file was submitted.'), + 'invalid': _('The submitted data was not a file. Check the encoding type on the form.'), + 'no_name': _('No filename could be determined.'), + 'empty': _('The submitted file is empty.'), + 'max_length': _('Ensure this filename has at most {max_length} characters (it has {length}).'), } use_url = api_settings.UPLOADED_FILES_USE_URL @@ -1118,7 +1118,7 @@ class ListField(Field): child = None initial = [] default_error_messages = { - 'not_a_list': _("Expected a list of items but got type `{input_type}`.") + 'not_a_list': _('Expected a list of items but got type `{input_type}`.') } def __init__(self, *args, **kwargs): @@ -1249,7 +1249,7 @@ class ModelField(Field): that do not have a serializer field to be mapped to. """ default_error_messages = { - 'max_length': _("Ensure this field has no more than {max_length} characters."), + 'max_length': _('Ensure this field has no more than {max_length} characters.'), } def __init__(self, model_field, **kwargs): diff --git a/rest_framework/generics.py b/rest_framework/generics.py index 738ba544..7ebed032 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -120,12 +120,12 @@ class GenericAPIView(views.APIView): if page == 'last': page_number = paginator.num_pages else: - raise NotFound(_("Choose a valid page number. Page numbers must be a whole number, or must be the string \"last\".")) + raise NotFound(_('Choose a valid page number. Page numbers must be a whole number, or must be the string "last".')) try: page = paginator.page(page_number) except InvalidPage as exc: - error_format = _("Invalid page \"{page_number}\": {message}.") + error_format = _('Invalid page "{page_number}": {message}.') raise NotFound(error_format.format( page_number=page_number, message=six.text_type(exc) )) diff --git a/rest_framework/locale/en_US/LC_MESSAGES/django.po b/rest_framework/locale/en_US/LC_MESSAGES/django.po index 5d0d3a04..c8fc7f4d 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: 2015-01-07 11:58+0000\n" +"POT-Creation-Date: 2015-01-07 12:28+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -42,11 +42,11 @@ msgid "Invalid token header. Token string should not contain spaces." msgstr "" #: authentication.py:168 -msgid "Invalid token" +msgid "Invalid token." msgstr "" #: authentication.py:171 -msgid "User inactive or deleted" +msgid "User inactive or deleted." msgstr "" #: authtoken/serializers.py:20 @@ -58,7 +58,7 @@ msgid "Unable to log in with provided credentials." msgstr "" #: authtoken/serializers.py:26 -msgid "Must include \"username\" and \"password\"" +msgid "Must include \"username\" and \"password\"." msgstr "" #: exceptions.py:38 @@ -86,7 +86,7 @@ msgid "Not found." msgstr "" #: exceptions.py:98 -msgid "Method '{method}' not allowed." +msgid "Method \"{method}\" not allowed." msgstr "" #: exceptions.py:109 @@ -94,7 +94,7 @@ msgid "Could not satisfy the request Accept header." msgstr "" #: exceptions.py:121 -msgid "Unsupported media type '{media_type}' in request." +msgid "Unsupported media type \"{media_type}\" in request." msgstr "" #: exceptions.py:134 @@ -136,7 +136,8 @@ msgstr "" #: fields.py:615 msgid "" -"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." msgstr "" #: fields.py:627 @@ -235,15 +236,15 @@ msgstr "" #: generics.py:123 msgid "" "Choose a valid page number. Page numbers must be a whole number, or must be " -"the string 'last'." +"the string \"last\"." msgstr "" #: generics.py:128 -msgid "Invalid page ({page_number}): {message}." +msgid "Invalid page \"{page_number}\": {message}." msgstr "" #: relations.py:132 -msgid "Invalid pk '{pk_value}' - object does not exist." +msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "" #: relations.py:133 @@ -299,7 +300,7 @@ msgid "This field must be unique for the \"{date_field}\" year." msgstr "" #: versioning.py:39 -msgid "Invalid version in 'Accept' header." +msgid "Invalid version in \"Accept\" header." msgstr "" #: versioning.py:70 versioning.py:112 diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 42b624e7..05ac3d1c 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -128,9 +128,9 @@ class StringRelatedField(RelatedField): class PrimaryKeyRelatedField(RelatedField): default_error_messages = { - 'required': _("This field is required."), - 'does_not_exist': _("Invalid pk \"{pk_value}\" - object does not exist."), - 'incorrect_type': _("Incorrect type. Expected pk value, received {data_type}."), + 'required': _('This field is required.'), + 'does_not_exist': _('Invalid pk "{pk_value}" - object does not exist.'), + 'incorrect_type': _('Incorrect type. Expected pk value, received {data_type}.'), } def use_pk_only_optimization(self): @@ -152,11 +152,11 @@ class HyperlinkedRelatedField(RelatedField): lookup_field = 'pk' default_error_messages = { - 'required': _("This field is required."), - 'no_match': _("Invalid hyperlink - No URL match."), - 'incorrect_match': _("Invalid hyperlink - Incorrect URL match."), - 'does_not_exist': _("Invalid hyperlink - Object does not exist."), - 'incorrect_type': _("Incorrect type. Expected URL string, received {data_type}."), + 'required': _('This field is required.'), + 'no_match': _('Invalid hyperlink - No URL match.'), + 'incorrect_match': _('Invalid hyperlink - Incorrect URL match.'), + 'does_not_exist': _('Invalid hyperlink - Object does not exist.'), + 'incorrect_type': _('Incorrect type. Expected URL string, received {data_type}.'), } def __init__(self, view_name=None, **kwargs): @@ -291,8 +291,8 @@ class SlugRelatedField(RelatedField): """ default_error_messages = { - 'does_not_exist': _("Object with {slug_name}={value} does not exist."), - 'invalid': _("Invalid value."), + 'does_not_exist': _('Object with {slug_name}={value} does not exist.'), + 'invalid': _('Invalid value.'), } def __init__(self, slug_field=None, **kwargs): diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 9d7c8884..623ed586 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -296,7 +296,7 @@ def get_validation_error_detail(exc): @six.add_metaclass(SerializerMetaclass) class Serializer(BaseSerializer): default_error_messages = { - 'invalid': _("Invalid data. Expected a dictionary, but got {datatype}.") + 'invalid': _('Invalid data. Expected a dictionary, but got {datatype}.') } @property @@ -473,7 +473,7 @@ class ListSerializer(BaseSerializer): many = True default_error_messages = { - 'not_a_list': _("Expected a list of items but got type `{input_type}`.") + 'not_a_list': _('Expected a list of items but got type `{input_type}`.') } def __init__(self, *args, **kwargs): diff --git a/rest_framework/validators.py b/rest_framework/validators.py index cf6f0718..e3719b8d 100644 --- a/rest_framework/validators.py +++ b/rest_framework/validators.py @@ -19,7 +19,7 @@ class UniqueValidator: Should be applied to an individual field on the serializer. """ - message = _("This field must be unique.") + message = _('This field must be unique.') def __init__(self, queryset, message=None): self.queryset = queryset @@ -73,8 +73,8 @@ class UniqueTogetherValidator: Should be applied to the serializer class, not to an individual field. """ - message = _("The fields {field_names} must make a unique set.") - missing_message = _("This field is required.") + message = _('The fields {field_names} must make a unique set.') + missing_message = _('This field is required.') def __init__(self, queryset, fields, message=None): self.queryset = queryset @@ -152,7 +152,7 @@ class UniqueTogetherValidator: class BaseUniqueForValidator: message = None - missing_message = _("This field is required.") + missing_message = _('This field is required.') def __init__(self, queryset, field, date_field, message=None): self.queryset = queryset @@ -216,7 +216,7 @@ class BaseUniqueForValidator: class UniqueForDateValidator(BaseUniqueForValidator): - message = _("This field must be unique for the \"{date_field}\" date.") + message = _('This field must be unique for the "{date_field}" date.') def filter_queryset(self, attrs, queryset): value = attrs[self.field] @@ -231,7 +231,7 @@ class UniqueForDateValidator(BaseUniqueForValidator): class UniqueForMonthValidator(BaseUniqueForValidator): - message = _("This field must be unique for the \"{date_field}\" month.") + message = _('This field must be unique for the "{date_field}" month.') def filter_queryset(self, attrs, queryset): value = attrs[self.field] @@ -244,7 +244,7 @@ class UniqueForMonthValidator(BaseUniqueForValidator): class UniqueForYearValidator(BaseUniqueForValidator): - message = _("This field must be unique for the \"{date_field}\" year.") + message = _('This field must be unique for the "{date_field}" year.') def filter_queryset(self, attrs, queryset): value = attrs[self.field] diff --git a/rest_framework/versioning.py b/rest_framework/versioning.py index 819c32df..e31c71e9 100644 --- a/rest_framework/versioning.py +++ b/rest_framework/versioning.py @@ -36,7 +36,7 @@ class AcceptHeaderVersioning(BaseVersioning): Host: example.com Accept: application/json; version=1.0 """ - invalid_version_message = _("Invalid version in \"Accept\" header.") + invalid_version_message = _('Invalid version in "Accept" header.') def determine_version(self, request, *args, **kwargs): media_type = _MediaType(request.accepted_media_type) @@ -67,7 +67,7 @@ class URLPathVersioning(BaseVersioning): Host: example.com Accept: application/json """ - invalid_version_message = _("Invalid version in URL path.") + invalid_version_message = _('Invalid version in URL path.') def determine_version(self, request, *args, **kwargs): version = kwargs.get(self.version_param, self.default_version) @@ -109,7 +109,7 @@ class NamespaceVersioning(BaseVersioning): Host: example.com Accept: application/json """ - invalid_version_message = _("Invalid version in URL path.") + invalid_version_message = _('Invalid version in URL path.') def determine_version(self, request, *args, **kwargs): resolver_match = getattr(request, 'resolver_match', None) @@ -135,7 +135,7 @@ class HostNameVersioning(BaseVersioning): Accept: application/json """ hostname_regex = re.compile(r'^([a-zA-Z0-9]+)\.[a-zA-Z0-9]+\.[a-zA-Z0-9]+$') - invalid_version_message = _("Invalid version in hostname.") + invalid_version_message = _('Invalid version in hostname.') def determine_version(self, request, *args, **kwargs): hostname, seperator, port = request.get_host().partition(':') @@ -157,7 +157,7 @@ class QueryParameterVersioning(BaseVersioning): Host: example.com Accept: application/json """ - invalid_version_message = _("Invalid version in query parameter.") + invalid_version_message = _('Invalid version in query parameter.') def determine_version(self, request, *args, **kwargs): version = request.query_params.get(self.version_param) -- cgit v1.2.3 From b7015ea8989d67617d276829ccb6a192362ee01f Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Wed, 7 Jan 2015 19:11:17 +0100 Subject: Bumped the version to 3.0.3. --- rest_framework/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index dec89b3e..fdcebb7b 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ ______ _____ _____ _____ __ """ __title__ = 'Django REST framework' -__version__ = '3.0.2' +__version__ = '3.0.3' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2015 Tom Christie' -- cgit v1.2.3 From 58ec7669aed9ebd58fd6095c6a6437bf9f3cf7f1 Mon Sep 17 00:00:00 2001 From: Craig Blaszczyk Date: Wed, 7 Jan 2015 18:22:30 +0000 Subject: swap backticks for double quotes --- rest_framework/exceptions.py | 2 +- rest_framework/fields.py | 12 ++++++------ rest_framework/locale/en_US/LC_MESSAGES/django.po | 8 ++++---- rest_framework/serializers.py | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py index f62c9fe3..f954c13e 100644 --- a/rest_framework/exceptions.py +++ b/rest_framework/exceptions.py @@ -52,7 +52,7 @@ class APIException(Exception): # built in `ValidationError`. For example: # # from rest_framework import serializers -# raise serializers.ValidationError("Value was invalid") +# raise serializers.ValidationError('Value was invalid') class ValidationError(APIException): status_code = status.HTTP_400_BAD_REQUEST diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 76101608..b80dea60 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -477,7 +477,7 @@ class Field(object): class BooleanField(Field): default_error_messages = { - 'invalid': _('`{input}` is not a valid boolean.') + 'invalid': _('"{input}" is not a valid boolean.') } default_empty_html = False initial = False @@ -505,7 +505,7 @@ class BooleanField(Field): class NullBooleanField(Field): default_error_messages = { - 'invalid': _('`{input}` is not a valid boolean.') + 'invalid': _('"{input}" is not a valid boolean.') } initial = None TRUE_VALUES = set(('t', 'T', 'true', 'True', 'TRUE', '1', 1, True)) @@ -972,7 +972,7 @@ class TimeField(Field): class ChoiceField(Field): default_error_messages = { - 'invalid_choice': _('`{input}` is not a valid choice.') + 'invalid_choice': _('"{input}" is not a valid choice.') } def __init__(self, choices, **kwargs): @@ -1016,8 +1016,8 @@ class ChoiceField(Field): class MultipleChoiceField(ChoiceField): default_error_messages = { - 'invalid_choice': _('`{input}` is not a valid choice.'), - 'not_a_list': _('Expected a list of items but got type `{input_type}`.') + 'invalid_choice': _('"{input}" is not a valid choice.'), + 'not_a_list': _('Expected a list of items but got type "{input_type}".') } default_empty_html = [] @@ -1118,7 +1118,7 @@ class ListField(Field): child = None initial = [] default_error_messages = { - 'not_a_list': _('Expected a list of items but got type `{input_type}`.') + 'not_a_list': _('Expected a list of items but got type "{input_type}".') } def __init__(self, *args, **kwargs): diff --git a/rest_framework/locale/en_US/LC_MESSAGES/django.po b/rest_framework/locale/en_US/LC_MESSAGES/django.po index c8fc7f4d..d98225ce 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: 2015-01-07 12:28+0000\n" +"POT-Creation-Date: 2015-01-07 18:21+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -111,7 +111,7 @@ msgid "This field may not be null." msgstr "" #: fields.py:480 fields.py:508 -msgid "`{input}` is not a valid boolean." +msgid "\"{input}\" is not a valid boolean." msgstr "" #: fields.py:543 @@ -199,11 +199,11 @@ msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" #: fields.py:975 fields.py:1019 -msgid "`{input}` is not a valid choice." +msgid "\"{input}\" is not a valid choice." msgstr "" #: fields.py:1020 fields.py:1121 serializers.py:476 -msgid "Expected a list of items but got type `{input_type}`." +msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" #: fields.py:1050 diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 623ed586..5bfbd235 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -473,7 +473,7 @@ class ListSerializer(BaseSerializer): many = True default_error_messages = { - 'not_a_list': _('Expected a list of items but got type `{input_type}`.') + 'not_a_list': _('Expected a list of items but got type "{input_type}".') } def __init__(self, *args, **kwargs): -- cgit v1.2.3 From 734f8f26678d3bd28f04bc44b0fabd146b97ddb0 Mon Sep 17 00:00:00 2001 From: Craig Blaszczyk Date: Wed, 7 Jan 2015 18:22:40 +0000 Subject: restore Django 404 --- rest_framework/generics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/generics.py b/rest_framework/generics.py index 7ebed032..d52f2b6c 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -120,13 +120,13 @@ class GenericAPIView(views.APIView): if page == 'last': page_number = paginator.num_pages else: - raise NotFound(_('Choose a valid page number. Page numbers must be a whole number, or must be the string "last".')) + raise Http404(_('Choose a valid page number. Page numbers must be a whole number, or must be the string "last".')) try: page = paginator.page(page_number) except InvalidPage as exc: error_format = _('Invalid page "{page_number}": {message}.') - raise NotFound(error_format.format( + raise Http404(error_format.format( page_number=page_number, message=six.text_type(exc) )) -- cgit v1.2.3 From b33a6cbff16e5a28a1a696e2ac617303da181720 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 8 Jan 2015 14:16:58 +0000 Subject: Ensure urlparse is not publically exposed in compat.py - less chance of accidental conflict. --- rest_framework/compat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/compat.py b/rest_framework/compat.py index b1f6f2fa..971dee9c 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -10,7 +10,7 @@ import inspect from django.core.exceptions import ImproperlyConfigured from django.utils.encoding import force_text -from django.utils.six.moves.urllib import parse as urlparse +from django.utils.six.moves.urllib.parse import urlparse as _urlparse from django.conf import settings from django.utils import six import django @@ -182,7 +182,7 @@ except ImportError: class RequestFactory(DjangoRequestFactory): def generic(self, method, path, data='', content_type='application/octet-stream', **extra): - parsed = urlparse.urlparse(path) + parsed = _urlparse(path) data = force_bytes_or_smart_bytes(data, settings.DEFAULT_CHARSET) r = { 'PATH_INFO': self._get_path(parsed), -- cgit v1.2.3 From 4d9e7a53565f6301b87999e6bafdb1c2c3c2af3b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 8 Jan 2015 15:38:27 +0000 Subject: Ammend docstring to use python2/3 compatible example. --- rest_framework/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 33f84813..fc6dfecd 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -167,7 +167,7 @@ class APISettings(object): For example: from rest_framework.settings import api_settings - print api_settings.DEFAULT_RENDERER_CLASSES + print(api_settings.DEFAULT_RENDERER_CLASSES) Any setting with string import paths will be automatically resolved and return the class, rather than the string literal. -- cgit v1.2.3 From 1368c31a705a4892995f42cf5e0dcdcbfa13a1ce Mon Sep 17 00:00:00 2001 From: Craig Blaszczyk Date: Thu, 8 Jan 2015 17:16:15 +0000 Subject: remove unused import --- rest_framework/generics.py | 1 - 1 file changed, 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/generics.py b/rest_framework/generics.py index d52f2b6c..0d709c37 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -10,7 +10,6 @@ from django.shortcuts import get_object_or_404 as _get_object_or_404 from django.utils import six from django.utils.translation import ugettext as _ from rest_framework import views, mixins -from rest_framework.exceptions import NotFound from rest_framework.settings import api_settings -- cgit v1.2.3 From 73feaf6299827607eab94ce96b77b73671880626 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 9 Jan 2015 15:30:36 +0000 Subject: First pass at 3.1 pagination API --- rest_framework/generics.py | 220 ++++++++++++++----------------------------- rest_framework/mixins.py | 13 +-- rest_framework/pagination.py | 219 +++++++++++++++++++++++++++++++----------- rest_framework/settings.py | 4 +- 4 files changed, 241 insertions(+), 215 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/generics.py b/rest_framework/generics.py index 0d709c37..12fb6413 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -2,29 +2,13 @@ Generic views that provide commonly needed behaviour. """ from __future__ import unicode_literals - -from django.core.paginator import Paginator, InvalidPage from django.db.models.query import QuerySet from django.http import Http404 from django.shortcuts import get_object_or_404 as _get_object_or_404 -from django.utils import six -from django.utils.translation import ugettext as _ from rest_framework import views, mixins from rest_framework.settings import api_settings -def strict_positive_int(integer_string, cutoff=None): - """ - Cast a string to a strictly positive integer. - """ - ret = int(integer_string) - if ret <= 0: - raise ValueError() - if cutoff: - ret = min(ret, cutoff) - return ret - - def get_object_or_404(queryset, *filter_args, **filter_kwargs): """ Same as Django's standard shortcut, but make sure to also raise 404 @@ -40,7 +24,6 @@ class GenericAPIView(views.APIView): """ Base class for all other generic views. """ - # You'll need to either set these attributes, # or override `get_queryset()`/`get_serializer_class()`. # If you are overriding a view method, it is important that you call @@ -50,146 +33,16 @@ class GenericAPIView(views.APIView): queryset = None serializer_class = None - # If you want to use object lookups other than pk, set this attribute. + # If you want to use object lookups other than pk, set 'lookup_field'. # For more complex lookup requirements override `get_object()`. lookup_field = 'pk' lookup_url_kwarg = None - # Pagination settings - paginate_by = api_settings.PAGINATE_BY - paginate_by_param = api_settings.PAGINATE_BY_PARAM - max_paginate_by = api_settings.MAX_PAGINATE_BY - pagination_serializer_class = api_settings.DEFAULT_PAGINATION_SERIALIZER_CLASS - page_kwarg = 'page' - # The filter backend classes to use for queryset filtering filter_backends = api_settings.DEFAULT_FILTER_BACKENDS - # The following attribute may be subject to change, - # and should be considered private API. - paginator_class = Paginator - - def get_serializer_context(self): - """ - Extra context provided to the serializer class. - """ - return { - 'request': self.request, - 'format': self.format_kwarg, - 'view': self - } - - def get_serializer(self, *args, **kwargs): - """ - Return the serializer instance that should be used for validating and - deserializing input, and for serializing output. - """ - serializer_class = self.get_serializer_class() - kwargs['context'] = self.get_serializer_context() - return serializer_class(*args, **kwargs) - - def get_pagination_serializer(self, page): - """ - Return a serializer instance to use with paginated data. - """ - class SerializerClass(self.pagination_serializer_class): - class Meta: - object_serializer_class = self.get_serializer_class() - - pagination_serializer_class = SerializerClass - context = self.get_serializer_context() - return pagination_serializer_class(instance=page, context=context) - - def paginate_queryset(self, queryset): - """ - Paginate a queryset if required, either returning a page object, - or `None` if pagination is not configured for this view. - """ - page_size = self.get_paginate_by() - if not page_size: - return None - - paginator = self.paginator_class(queryset, page_size) - page_kwarg = self.kwargs.get(self.page_kwarg) - page_query_param = self.request.query_params.get(self.page_kwarg) - page = page_kwarg or page_query_param or 1 - try: - page_number = paginator.validate_number(page) - except InvalidPage: - if page == 'last': - page_number = paginator.num_pages - else: - raise Http404(_('Choose a valid page number. Page numbers must be a whole number, or must be the string "last".')) - - try: - page = paginator.page(page_number) - except InvalidPage as exc: - error_format = _('Invalid page "{page_number}": {message}.') - raise Http404(error_format.format( - page_number=page_number, message=six.text_type(exc) - )) - - return page - - def filter_queryset(self, queryset): - """ - Given a queryset, filter it with whichever filter backend is in use. - - You are unlikely to want to override this method, although you may need - to call it either from a list view, or from a custom `get_object` - method if you want to apply the configured filtering backend to the - default queryset. - """ - for backend in self.get_filter_backends(): - queryset = backend().filter_queryset(self.request, queryset, self) - return queryset - - def get_filter_backends(self): - """ - Returns the list of filter backends that this view requires. - """ - return list(self.filter_backends) - - # The following methods provide default implementations - # that you may want to override for more complex cases. - - def get_paginate_by(self): - """ - Return the size of pages to use with pagination. - - If `PAGINATE_BY_PARAM` is set it will attempt to get the page size - from a named query parameter in the url, eg. ?page_size=100 - - Otherwise defaults to using `self.paginate_by`. - """ - if self.paginate_by_param: - try: - return strict_positive_int( - self.request.query_params[self.paginate_by_param], - cutoff=self.max_paginate_by - ) - except (KeyError, ValueError): - pass - - return self.paginate_by - - def get_serializer_class(self): - """ - Return the class to use for the serializer. - Defaults to using `self.serializer_class`. - - You may want to override this if you need to provide different - serializations depending on the incoming request. - - (Eg. admins get full serialization, others get basic serialization) - """ - assert self.serializer_class is not None, ( - "'%s' should either include a `serializer_class` attribute, " - "or override the `get_serializer_class()` method." - % self.__class__.__name__ - ) - - return self.serializer_class + # The style to use for queryset pagination. + pagination_class = api_settings.DEFAULT_PAGINATION_CLASS def get_queryset(self): """ @@ -246,6 +99,73 @@ class GenericAPIView(views.APIView): return obj + def get_serializer(self, *args, **kwargs): + """ + Return the serializer instance that should be used for validating and + deserializing input, and for serializing output. + """ + serializer_class = self.get_serializer_class() + kwargs['context'] = self.get_serializer_context() + return serializer_class(*args, **kwargs) + + def get_serializer_class(self): + """ + Return the class to use for the serializer. + Defaults to using `self.serializer_class`. + + You may want to override this if you need to provide different + serializations depending on the incoming request. + + (Eg. admins get full serialization, others get basic serialization) + """ + assert self.serializer_class is not None, ( + "'%s' should either include a `serializer_class` attribute, " + "or override the `get_serializer_class()` method." + % self.__class__.__name__ + ) + + return self.serializer_class + + def get_serializer_context(self): + """ + Extra context provided to the serializer class. + """ + return { + 'request': self.request, + 'format': self.format_kwarg, + 'view': self + } + + def filter_queryset(self, queryset): + """ + Given a queryset, filter it with whichever filter backend is in use. + + You are unlikely to want to override this method, although you may need + to call it either from a list view, or from a custom `get_object` + method if you want to apply the configured filtering backend to the + default queryset. + """ + for backend in list(self.filter_backends): + queryset = backend().filter_queryset(self.request, queryset, self) + return queryset + + @property + def pager(self): + if not hasattr(self, '_pager'): + if self.pagination_class is None: + self._pager = None + else: + self._pager = self.pagination_class() + return self._pager + + def paginate_queryset(self, queryset): + if self.pager is None: + return None + return self.pager.paginate_queryset(queryset, self.request, view=self) + + def get_paginated_response(self, objects): + return self.pager.get_paginated_response(objects) + # Concrete view classes that provide method handlers # by composing the mixin classes with the base view. diff --git a/rest_framework/mixins.py b/rest_framework/mixins.py index 2074a107..c34cfcee 100644 --- a/rest_framework/mixins.py +++ b/rest_framework/mixins.py @@ -5,7 +5,6 @@ We don't bind behaviour to http method handlers yet, which allows mixin classes to be composed in interesting ways. """ from __future__ import unicode_literals - from rest_framework import status from rest_framework.response import Response from rest_framework.settings import api_settings @@ -37,12 +36,14 @@ class ListModelMixin(object): List a queryset. """ def list(self, request, *args, **kwargs): - instance = self.filter_queryset(self.get_queryset()) - page = self.paginate_queryset(instance) + queryset = self.filter_queryset(self.get_queryset()) + + page = self.paginate_queryset(queryset) if page is not None: - serializer = self.get_pagination_serializer(page) - else: - serializer = self.get_serializer(instance, many=True) + serializer = self.get_serializer(page, many=True) + return self.get_paginated_response(serializer.data) + + serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index f31e5fa4..da2d60a4 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -3,87 +3,192 @@ Pagination serializers determine the structure of the output that should be used for paginated responses. """ from __future__ import unicode_literals -from rest_framework import serializers +from django.core.paginator import InvalidPage, Paginator as DjangoPaginator +from django.utils import six +from django.utils.translation import ugettext as _ +from rest_framework.compat import OrderedDict +from rest_framework.exceptions import NotFound +from rest_framework.response import Response +from rest_framework.settings import api_settings from rest_framework.templatetags.rest_framework import replace_query_param -class NextPageField(serializers.Field): +def _strict_positive_int(integer_string, cutoff=None): """ - Field that returns a link to the next page in paginated results. + Cast a string to a strictly positive integer. """ - page_field = 'page' - - def to_representation(self, value): - if not value.has_next(): - return None - page = value.next_page_number() - request = self.context.get('request') - url = request and request.build_absolute_uri() or '' - return replace_query_param(url, self.page_field, page) + ret = int(integer_string) + if ret <= 0: + raise ValueError() + if cutoff: + ret = min(ret, cutoff) + return ret -class PreviousPageField(serializers.Field): - """ - Field that returns a link to the previous page in paginated results. - """ - page_field = 'page' +class BasePagination(object): + def paginate_queryset(self, queryset, request): + raise NotImplemented('paginate_queryset() must be implemented.') - def to_representation(self, value): - if not value.has_previous(): - return None - page = value.previous_page_number() - request = self.context.get('request') - url = request and request.build_absolute_uri() or '' - return replace_query_param(url, self.page_field, page) + def get_paginated_response(self, data, page, request): + raise NotImplemented('get_paginated_response() must be implemented.') -class DefaultObjectSerializer(serializers.ReadOnlyField): +class PageNumberPagination(BasePagination): """ - If no object serializer is specified, then this serializer will be applied - as the default. + A simple page number based style that supports page numbers as + query parameters. For example: + + http://api.example.org/accounts/?page=4 + http://api.example.org/accounts/?page=4&page_size=100 """ + # The default page size. + # Defaults to `None`, meaning pagination is disabled. + paginate_by = api_settings.PAGINATE_BY - def __init__(self, source=None, many=None, context=None): - # Note: Swallow context and many kwargs - only required for - # eg. ModelSerializer. - super(DefaultObjectSerializer, self).__init__(source=source) + # Client can control the page using this query parameter. + page_query_param = 'page' + # Client can control the page size using this query parameter. + # Default is 'None'. Set to eg 'page_size' to enable usage. + paginate_by_param = api_settings.PAGINATE_BY_PARAM -class BasePaginationSerializer(serializers.Serializer): - """ - A base class for pagination serializers to inherit from, - to make implementing custom serializers more easy. - """ - results_field = 'results' + # Set to an integer to limit the maximum page size the client may request. + # Only relevant if 'paginate_by_param' has also been set. + max_paginate_by = api_settings.MAX_PAGINATE_BY - def __init__(self, *args, **kwargs): + def paginate_queryset(self, queryset, request, view): """ - Override init to add in the object serializer field on-the-fly. + Paginate a queryset if required, either returning a page object, + or `None` if pagination is not configured for this view. """ - super(BasePaginationSerializer, self).__init__(*args, **kwargs) - results_field = self.results_field + for attr in ( + 'paginate_by', 'page_query_param', + 'paginate_by_param', 'max_paginate_by' + ): + if hasattr(view, attr): + setattr(self, attr, getattr(view, attr)) + + page_size = self.get_page_size(request) + if not page_size: + return None + paginator = DjangoPaginator(queryset, page_size) + page_string = request.query_params.get(self.page_query_param, 1) try: - object_serializer = self.Meta.object_serializer_class - except AttributeError: - object_serializer = DefaultObjectSerializer + page_number = paginator.validate_number(page_string) + except InvalidPage: + if page_string == 'last': + page_number = paginator.num_pages + else: + msg = _( + 'Choose a valid page number. Page numbers must be a ' + 'whole number, or must be the string "last".' + ) + raise NotFound(msg) try: - list_serializer_class = object_serializer.Meta.list_serializer_class - except AttributeError: - list_serializer_class = serializers.ListSerializer + self.page = paginator.page(page_number) + except InvalidPage as exc: + msg = _('Invalid page "{page_number}": {message}.').format( + page_number=page_number, message=six.text_type(exc) + ) + raise NotFound(msg) + + self.request = request + return self.page + + def get_paginated_response(self, objects): + return Response(OrderedDict([ + ('count', self.page.paginator.count), + ('next', self.get_next_link()), + ('previous', self.get_previous_link()), + ('results', objects) + ])) + + def get_page_size(self, request): + if self.paginate_by_param: + try: + return _strict_positive_int( + request.query_params[self.paginate_by_param], + cutoff=self.max_paginate_by + ) + except (KeyError, ValueError): + pass + + return self.paginate_by + + def get_next_link(self): + if not self.page.has_next(): + return None + url = self.request.build_absolute_uri() + page_number = self.page.next_page_number() + return replace_query_param(url, self.page_query_param, page_number) - self.fields[results_field] = list_serializer_class( - child=object_serializer(), - source='object_list' - ) - self.fields[results_field].bind(field_name=results_field, parent=self) + def get_previous_link(self): + if not self.page.has_previous(): + return None + url = self.request.build_absolute_uri() + page_number = self.page.previous_page_number() + return replace_query_param(url, self.page_query_param, page_number) -class PaginationSerializer(BasePaginationSerializer): +class LimitOffsetPagination(BasePagination): """ - A default implementation of a pagination serializer. + A limit/offset based style. For example: + + http://api.example.org/accounts/?limit=100 + http://api.example.org/accounts/?offset=400&limit=100 """ - count = serializers.ReadOnlyField(source='paginator.count') - next = NextPageField(source='*') - previous = PreviousPageField(source='*') + default_limit = api_settings.PAGINATE_BY + limit_query_param = 'limit' + offset_query_param = 'offset' + max_limit = None + + def paginate_queryset(self, queryset, request, view): + self.limit = self.get_limit(request) + self.offset = self.get_offset(request) + self.count = queryset.count() + self.request = request + return queryset[self.offset:self.offset + self.limit] + + def get_paginated_response(self, objects): + return Response(OrderedDict([ + ('count', self.count), + ('next', self.get_next_link()), + ('previous', self.get_previous_link()), + ('results', objects) + ])) + + def get_limit(self, request): + if self.limit_query_param: + try: + return _strict_positive_int( + request.query_params[self.limit_query_param], + cutoff=self.max_limit + ) + except (KeyError, ValueError): + pass + + return self.default_limit + + def get_offset(self, request): + try: + return _strict_positive_int( + request.query_params[self.offset_query_param], + ) + except (KeyError, ValueError): + return 0 + + def get_next_link(self, page): + if self.offset + self.limit >= self.count: + return None + url = self.request.build_absolute_uri() + offset = self.offset + self.limit + return replace_query_param(url, self.offset_query_param, offset) + + def get_previous_link(self, page): + if self.offset - self.limit < 0: + return None + url = self.request.build_absolute_uri() + offset = self.offset - self.limit + return replace_query_param(url, self.offset_query_param, offset) diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 877d461b..3cce26b1 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -49,7 +49,7 @@ DEFAULTS = { 'DEFAULT_VERSIONING_CLASS': None, # Generic view behavior - 'DEFAULT_PAGINATION_SERIALIZER_CLASS': 'rest_framework.pagination.PaginationSerializer', + 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'DEFAULT_FILTER_BACKENDS': (), # Throttling @@ -130,7 +130,7 @@ IMPORT_STRINGS = ( 'DEFAULT_CONTENT_NEGOTIATION_CLASS', 'DEFAULT_METADATA_CLASS', 'DEFAULT_VERSIONING_CLASS', - 'DEFAULT_PAGINATION_SERIALIZER_CLASS', + 'DEFAULT_PAGINATION_CLASS', 'DEFAULT_FILTER_BACKENDS', 'EXCEPTION_HANDLER', 'TEST_REQUEST_RENDERER_CLASSES', -- cgit v1.2.3 From d6bff10f9829b3cef1c2773c303b172a8c7ec525 Mon Sep 17 00:00:00 2001 From: Ask Holme Date: Sat, 10 Jan 2015 18:15:21 +0100 Subject: Make FileUploadParser work with standard django API Output from parsers ends up in a Django MergeDict and they exists elements to be dicts - not None--- rest_framework/parsers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py index 3e3395c0..401856ec 100644 --- a/rest_framework/parsers.py +++ b/rest_framework/parsers.py @@ -277,7 +277,7 @@ class FileUploadParser(BaseParser): for index, handler in enumerate(upload_handlers): file_obj = handler.file_complete(counters[index]) if file_obj: - return DataAndFiles(None, {'file': file_obj}) + return DataAndFiles({}, {'file': file_obj}) raise ParseError("FileUpload parse error - " "none of upload handlers can handle the stream") -- cgit v1.2.3 From d6d08db0dd16f4a4a93b69ecf1c5948f375335b0 Mon Sep 17 00:00:00 2001 From: José Padilla Date: Sun, 11 Jan 2015 10:55:56 -0400 Subject: Fix ident format when using HTTP_X_FORWARDED_FOR If NUM_PROXIES setting is set to None, HTTP_X_FORWARDED_FOR might be used as is, which might contain spaces and cause errors on cache backends like memcached. --- rest_framework/throttling.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/throttling.py b/rest_framework/throttling.py index 361dbddf..7dfe2f96 100644 --- a/rest_framework/throttling.py +++ b/rest_framework/throttling.py @@ -35,7 +35,7 @@ class BaseThrottle(object): client_addr = addrs[-min(num_proxies, len(xff))] return client_addr.strip() - return xff if xff else remote_addr + return ''.join(xff.split()) if xff else remote_addr def wait(self): """ @@ -173,12 +173,6 @@ class AnonRateThrottle(SimpleRateThrottle): if request.user.is_authenticated(): return None # Only throttle unauthenticated requests. - ident = request.META.get('HTTP_X_FORWARDED_FOR') - if ident is None: - ident = request.META.get('REMOTE_ADDR') - else: - ident = ''.join(ident.split()) - return self.cache_format % { 'scope': self.scope, 'ident': self.get_ident(request) -- cgit v1.2.3 From cc13ee0577fb3de9602da634ab9c835749da49c4 Mon Sep 17 00:00:00 2001 From: José Padilla Date: Mon, 12 Jan 2015 08:12:24 -0400 Subject: Fix error when NUM_PROXIES is greater than one --- rest_framework/throttling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/throttling.py b/rest_framework/throttling.py index 7dfe2f96..0f10136d 100644 --- a/rest_framework/throttling.py +++ b/rest_framework/throttling.py @@ -32,7 +32,7 @@ class BaseThrottle(object): if num_proxies == 0 or xff is None: return remote_addr addrs = xff.split(',') - client_addr = addrs[-min(num_proxies, len(xff))] + client_addr = addrs[-min(num_proxies, len(addrs))] return client_addr.strip() return ''.join(xff.split()) if xff else remote_addr -- cgit v1.2.3 From 1bcec3a0ac4346b31b655a08505d3e3dc2156604 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 13 Jan 2015 17:14:13 +0000 Subject: API tweaks and pagination documentation --- rest_framework/generics.py | 6 +++--- rest_framework/pagination.py | 28 +++++++++++++++++++--------- 2 files changed, 22 insertions(+), 12 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/generics.py b/rest_framework/generics.py index 12fb6413..cdf6ece0 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -160,11 +160,11 @@ class GenericAPIView(views.APIView): def paginate_queryset(self, queryset): if self.pager is None: - return None + return queryset return self.pager.paginate_queryset(queryset, self.request, view=self) - def get_paginated_response(self, objects): - return self.pager.get_paginated_response(objects) + def get_paginated_response(self, data): + return self.pager.get_paginated_response(data) # Concrete view classes that provide method handlers diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index da2d60a4..b9d48796 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -25,11 +25,21 @@ def _strict_positive_int(integer_string, cutoff=None): return ret +def _get_count(queryset): + """ + Determine an object count, supporting either querysets or regular lists. + """ + try: + return queryset.count() + except AttributeError: + return len(queryset) + + class BasePagination(object): - def paginate_queryset(self, queryset, request): + def paginate_queryset(self, queryset, request, view): raise NotImplemented('paginate_queryset() must be implemented.') - def get_paginated_response(self, data, page, request): + def get_paginated_response(self, data): raise NotImplemented('get_paginated_response() must be implemented.') @@ -58,8 +68,8 @@ class PageNumberPagination(BasePagination): def paginate_queryset(self, queryset, request, view): """ - Paginate a queryset if required, either returning a page object, - or `None` if pagination is not configured for this view. + Paginate a queryset if required, either returning a + page object, or `None` if pagination is not configured for this view. """ for attr in ( 'paginate_by', 'page_query_param', @@ -97,12 +107,12 @@ class PageNumberPagination(BasePagination): self.request = request return self.page - def get_paginated_response(self, objects): + def get_paginated_response(self, data): return Response(OrderedDict([ ('count', self.page.paginator.count), ('next', self.get_next_link()), ('previous', self.get_previous_link()), - ('results', objects) + ('results', data) ])) def get_page_size(self, request): @@ -147,16 +157,16 @@ class LimitOffsetPagination(BasePagination): def paginate_queryset(self, queryset, request, view): self.limit = self.get_limit(request) self.offset = self.get_offset(request) - self.count = queryset.count() + self.count = _get_count(queryset) self.request = request return queryset[self.offset:self.offset + self.limit] - def get_paginated_response(self, objects): + def get_paginated_response(self, data): return Response(OrderedDict([ ('count', self.count), ('next', self.get_next_link()), ('previous', self.get_previous_link()), - ('results', objects) + ('results', data) ])) def get_limit(self, request): -- cgit v1.2.3 From 4ce4132e08ba7764f120c71eeebdbaefee281689 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 14 Jan 2015 12:56:03 +0000 Subject: Preserve ordering on relationship drop-down choices. Closes #2408. --- rest_framework/relations.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 7b119291..aa0c2def 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -7,6 +7,7 @@ from django.utils import six from django.utils.encoding import 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 OrderedDict from rest_framework.fields import get_attribute, empty, Field from rest_framework.reverse import reverse from rest_framework.utils import html @@ -103,7 +104,7 @@ class RelatedField(Field): @property def choices(self): - return dict([ + return OrderedDict([ ( six.text_type(self.to_representation(item)), six.text_type(item) @@ -364,7 +365,7 @@ class ManyRelatedField(Field): (item, self.child_relation.to_representation(item)) for item in iterable ] - return dict([ + return OrderedDict([ ( six.text_type(item_representation), six.text_type(item) + ' - ' + six.text_type(item_representation) -- cgit v1.2.3 From 4d287c7aef7b12086930eeb7a05cadb7e8b2cc48 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 14 Jan 2015 13:19:56 +0000 Subject: Include paragraph around view description in browable API --- rest_framework/utils/formatting.py | 1 + 1 file changed, 1 insertion(+) (limited to 'rest_framework') diff --git a/rest_framework/utils/formatting.py b/rest_framework/utils/formatting.py index 470af51b..173848df 100644 --- a/rest_framework/utils/formatting.py +++ b/rest_framework/utils/formatting.py @@ -59,4 +59,5 @@ def markup_description(description): description = apply_markdown(description) else: description = escape(description).replace('\n', '
') + description = '

' + description + '

' return mark_safe(description) -- cgit v1.2.3 From f13fcba9a9f41f7e00e0ea8956fcc65ca168c76c Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 14 Jan 2015 13:20:02 +0000 Subject: Include paragraph around view description in browable API --- rest_framework/utils/formatting.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/utils/formatting.py b/rest_framework/utils/formatting.py index 173848df..8b6f005e 100644 --- a/rest_framework/utils/formatting.py +++ b/rest_framework/utils/formatting.py @@ -2,12 +2,10 @@ Utility functions to return a formatted name and description for a given view. """ from __future__ import unicode_literals -import re - from django.utils.html import escape from django.utils.safestring import mark_safe - from rest_framework.compat import apply_markdown, force_text +import re def remove_trailing_string(content, trailing): -- cgit v1.2.3 From 3833a5bb8a9174e5fb09dac59a964eff24b6065e Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 14 Jan 2015 16:51:26 +0000 Subject: Include pagination control in browsable API --- rest_framework/pagination.py | 90 +++++++++++++++++++++- rest_framework/renderers.py | 1 + .../static/rest_framework/css/bootstrap-tweaks.css | 4 - rest_framework/templates/rest_framework/base.html | 9 +++ .../rest_framework/pagination/numbers.html | 27 +++++++ rest_framework/templatetags/rest_framework.py | 17 ++++ 6 files changed, 143 insertions(+), 5 deletions(-) create mode 100644 rest_framework/templates/rest_framework/pagination/numbers.html (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index b9d48796..bd343c0d 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -3,14 +3,18 @@ Pagination serializers determine the structure of the output that should be used for paginated responses. """ from __future__ import unicode_literals +from collections import namedtuple from django.core.paginator import InvalidPage, Paginator as DjangoPaginator +from django.template import Context, loader from django.utils import six from django.utils.translation import ugettext as _ from rest_framework.compat import OrderedDict from rest_framework.exceptions import NotFound from rest_framework.response import Response from rest_framework.settings import api_settings -from rest_framework.templatetags.rest_framework import replace_query_param +from rest_framework.templatetags.rest_framework import ( + replace_query_param, remove_query_param +) def _strict_positive_int(integer_string, cutoff=None): @@ -35,6 +39,49 @@ def _get_count(queryset): return len(queryset) +def _get_displayed_page_numbers(current, final): + """ + This utility function determines a list of page numbers to display. + This gives us a nice contextually relevant set of page numbers. + + For example: + current=14, final=16 -> [1, None, 13, 14, 15, 16] + """ + assert current >= 1 + assert final >= current + + # We always include the first two pages, last two pages, and + # two pages either side of the current page. + included = set(( + 1, + current - 1, current, current + 1, + final + )) + + # If the break would only exclude a single page number then we + # may as well include the page number instead of the break. + if current == 4: + included.add(2) + if current == final - 3: + included.add(final - 1) + + # Now sort the page numbers and drop anything outside the limits. + included = [ + idx for idx in sorted(list(included)) + if idx > 0 and idx <= final + ] + + # Finally insert any `...` breaks + if current > 4: + included.insert(1, None) + if current < final - 3: + included.insert(len(included) - 1, None) + return included + + +PageLink = namedtuple('PageLink', ['url', 'number', 'is_active', 'is_break']) + + class BasePagination(object): def paginate_queryset(self, queryset, request, view): raise NotImplemented('paginate_queryset() must be implemented.') @@ -66,6 +113,8 @@ class PageNumberPagination(BasePagination): # Only relevant if 'paginate_by_param' has also been set. max_paginate_by = api_settings.MAX_PAGINATE_BY + template = 'rest_framework/pagination/numbers.html' + def paginate_queryset(self, queryset, request, view): """ Paginate a queryset if required, either returning a @@ -104,6 +153,8 @@ class PageNumberPagination(BasePagination): ) raise NotFound(msg) + # Indicate that the browsable API should display pagination controls. + self.mark_as_used = True self.request = request return self.page @@ -139,8 +190,45 @@ class PageNumberPagination(BasePagination): return None url = self.request.build_absolute_uri() page_number = self.page.previous_page_number() + if page_number == 1: + return remove_query_param(url, self.page_query_param) return replace_query_param(url, self.page_query_param, page_number) + def to_html(self): + current = self.page.number + final = self.page.paginator.num_pages + + page_links = [] + base_url = self.request.build_absolute_uri() + for page_number in _get_displayed_page_numbers(current, final): + if page_number is None: + page_link = PageLink( + url=None, + number=None, + is_active=False, + is_break=True + ) + else: + if page_number == 1: + url = remove_query_param(base_url, self.page_query_param) + else: + url = replace_query_param(url, self.page_query_param, page_number) + page_link = PageLink( + url=url, + number=page_number, + is_active=(page_number == current), + is_break=False + ) + page_links.append(page_link) + + template = loader.get_template(self.template) + context = Context({ + 'previous_url': self.get_previous_link(), + 'next_url': self.get_next_link(), + 'page_links': page_links + }) + return template.render(context) + class LimitOffsetPagination(BasePagination): """ diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index c4de30db..4c002b16 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -592,6 +592,7 @@ class BrowsableAPIRenderer(BaseRenderer): 'description': self.get_description(view), 'name': self.get_name(view), 'version': VERSION, + 'pager': getattr(view, 'pager', None), 'breadcrumblist': self.get_breadcrumbs(request), 'allowed_methods': view.allowed_methods, 'available_formats': [renderer_cls.format for renderer_cls in view.renderer_classes], diff --git a/rest_framework/static/rest_framework/css/bootstrap-tweaks.css b/rest_framework/static/rest_framework/css/bootstrap-tweaks.css index 36c7be48..d4a7d31a 100644 --- a/rest_framework/static/rest_framework/css/bootstrap-tweaks.css +++ b/rest_framework/static/rest_framework/css/bootstrap-tweaks.css @@ -185,10 +185,6 @@ body a:hover { color: #c20000; } -#content a span { - text-decoration: underline; - } - .request-info { clear:both; } diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html index e9668193..e0030981 100644 --- a/rest_framework/templates/rest_framework/base.html +++ b/rest_framework/templates/rest_framework/base.html @@ -119,9 +119,18 @@ +
{% block description %} {{ description }} {% endblock %} +
+ + {% if pager.mark_as_used %} + + {% endif %} +
{{ request.method }} {{ request.get_full_path }}
diff --git a/rest_framework/templates/rest_framework/pagination/numbers.html b/rest_framework/templates/rest_framework/pagination/numbers.html new file mode 100644 index 00000000..04045810 --- /dev/null +++ b/rest_framework/templates/rest_framework/pagination/numbers.html @@ -0,0 +1,27 @@ +
    + {% if previous_url %} +
  • + {% else %} +
  • + {% endif %} + + {% for page_link in page_links %} + {% if page_link.is_break %} +
  • + +
  • + {% else %} + {% if page_link.is_active %} +
  • {{ page_link.number }}
  • + {% else %} +
  • {{ page_link.number }}
  • + {% endif %} + {% endif %} + {% endfor %} + + {% if next_url %} +
  • + {% else %} +
  • + {% endif %} +
diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index 69e03af4..bf159d8b 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -26,6 +26,23 @@ def replace_query_param(url, key, val): return urlparse.urlunsplit((scheme, netloc, path, query, fragment)) +def remove_query_param(url, key): + """ + Given a URL and a key/val pair, set or replace an item in the query + parameters of the URL, and return the new URL. + """ + (scheme, netloc, path, query, fragment) = urlparse.urlsplit(url) + query_dict = QueryDict(query).copy() + query_dict.pop(key, None) + query = query_dict.urlencode() + return urlparse.urlunsplit((scheme, netloc, path, query, fragment)) + + +@register.simple_tag +def get_pagination_html(pager): + return pager.to_html() + + # Regex for adding classes to html snippets class_re = re.compile(r'(?<=class=["\'])(.*)(?=["\'])') -- cgit v1.2.3 From 313aa727e3c44016e531a7af75051fc6e6d7cb96 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 14 Jan 2015 17:46:41 +0000 Subject: Tweaks --- rest_framework/pagination.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index bd343c0d..69d0f77d 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -46,6 +46,12 @@ def _get_displayed_page_numbers(current, final): For example: current=14, final=16 -> [1, None, 13, 14, 15, 16] + + This implementation gives one page to each side of the cursor, + for an implementation which gives two pages to each side of the cursor, + which is a copy of how GitHub treat pagination in their issue lists, see: + + https://gist.github.com/tomchristie/321140cebb1c4a558b15 """ assert current >= 1 assert final >= current @@ -60,10 +66,12 @@ def _get_displayed_page_numbers(current, final): # If the break would only exclude a single page number then we # may as well include the page number instead of the break. - if current == 4: + if current <= 4: included.add(2) - if current == final - 3: + included.add(3) + if current >= final - 3: included.add(final - 1) + included.add(final - 2) # Now sort the page numbers and drop anything outside the limits. included = [ -- cgit v1.2.3 From d76e83dd78627a0cf4bcd4b28a7710fb678d8d4e Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 15 Jan 2015 16:52:07 +0000 Subject: Tweaks, and add pagination controls for offset/limit. --- rest_framework/generics.py | 16 +-- rest_framework/pagination.py | 126 ++++++++++++++++----- rest_framework/renderers.py | 7 +- .../static/rest_framework/css/bootstrap-tweaks.css | 7 ++ rest_framework/templates/rest_framework/base.html | 4 +- 5 files changed, 119 insertions(+), 41 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/generics.py b/rest_framework/generics.py index cdf6ece0..4cc4c64d 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -150,21 +150,21 @@ class GenericAPIView(views.APIView): return queryset @property - def pager(self): - if not hasattr(self, '_pager'): + def paginator(self): + if not hasattr(self, '_paginator'): if self.pagination_class is None: - self._pager = None + self._paginator = None else: - self._pager = self.pagination_class() - return self._pager + self._paginator = self.pagination_class() + return self._paginator def paginate_queryset(self, queryset): - if self.pager is None: + if self.paginator is None: return queryset - return self.pager.paginate_queryset(queryset, self.request, view=self) + return self.paginator.paginate_queryset(queryset, self.request, view=self) def get_paginated_response(self, data): - return self.pager.get_paginated_response(data) + return self.paginator.get_paginated_response(data) # Concrete view classes that provide method handlers diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 69d0f77d..2b78f1f7 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -29,6 +29,15 @@ def _strict_positive_int(integer_string, cutoff=None): return ret +def _divide_with_ceil(a, b): + """ + Returns 'a' divded by 'b', with any remainder rounded up. + """ + if a % b: + return (a / b) + 1 + return a / b + + def _get_count(queryset): """ Determine an object count, supporting either querysets or regular lists. @@ -48,14 +57,21 @@ def _get_displayed_page_numbers(current, final): current=14, final=16 -> [1, None, 13, 14, 15, 16] This implementation gives one page to each side of the cursor, - for an implementation which gives two pages to each side of the cursor, - which is a copy of how GitHub treat pagination in their issue lists, see: + or two pages to the side when the cursor is at the edge, then + ensures that any breaks between non-continous page numbers never + remove only a single page. + + For an alernativative implementation which gives two pages to each side of + the cursor, eg. as in GitHub issue list pagination, see: https://gist.github.com/tomchristie/321140cebb1c4a558b15 """ assert current >= 1 assert final >= current + if final <= 5: + return range(1, final + 1) + # We always include the first two pages, last two pages, and # two pages either side of the current page. included = set(( @@ -87,16 +103,46 @@ def _get_displayed_page_numbers(current, final): return included +def _get_page_links(page_numbers, current, url_func): + """ + Given a list of page numbers and `None` page breaks, + return a list of `PageLink` objects. + """ + page_links = [] + for page_number in page_numbers: + if page_number is None: + page_link = PageLink( + url=None, + number=None, + is_active=False, + is_break=True + ) + else: + page_link = PageLink( + url=url_func(page_number), + number=page_number, + is_active=(page_number == current), + is_break=False + ) + page_links.append(page_link) + return page_links + + PageLink = namedtuple('PageLink', ['url', 'number', 'is_active', 'is_break']) class BasePagination(object): + display_page_controls = False + def paginate_queryset(self, queryset, request, view): raise NotImplemented('paginate_queryset() must be implemented.') def get_paginated_response(self, data): raise NotImplemented('get_paginated_response() must be implemented.') + def to_html(self): + raise NotImplemented('to_html() must be implemented to display page controls.') + class PageNumberPagination(BasePagination): """ @@ -161,8 +207,9 @@ class PageNumberPagination(BasePagination): ) raise NotFound(msg) - # Indicate that the browsable API should display pagination controls. - self.mark_as_used = True + if paginator.count > 1: + # The browsable API should display pagination controls. + self.display_page_controls = True self.request = request return self.page @@ -203,31 +250,17 @@ class PageNumberPagination(BasePagination): return replace_query_param(url, self.page_query_param, page_number) def to_html(self): - current = self.page.number - final = self.page.paginator.num_pages - - page_links = [] base_url = self.request.build_absolute_uri() - for page_number in _get_displayed_page_numbers(current, final): - if page_number is None: - page_link = PageLink( - url=None, - number=None, - is_active=False, - is_break=True - ) + def page_number_to_url(page_number): + if page_number == 1: + return remove_query_param(base_url, self.page_query_param) else: - if page_number == 1: - url = remove_query_param(base_url, self.page_query_param) - else: - url = replace_query_param(url, self.page_query_param, page_number) - page_link = PageLink( - url=url, - number=page_number, - is_active=(page_number == current), - is_break=False - ) - page_links.append(page_link) + return replace_query_param(base_url, self.page_query_param, page_number) + + current = self.page.number + final = self.page.paginator.num_pages + page_numbers = _get_displayed_page_numbers(current, final) + page_links = _get_page_links(page_numbers, current, page_number_to_url) template = loader.get_template(self.template) context = Context({ @@ -250,11 +283,15 @@ class LimitOffsetPagination(BasePagination): offset_query_param = 'offset' max_limit = None + template = 'rest_framework/pagination/numbers.html' + def paginate_queryset(self, queryset, request, view): self.limit = self.get_limit(request) self.offset = self.get_offset(request) self.count = _get_count(queryset) self.request = request + if self.count > self.limit: + self.display_page_controls = True return queryset[self.offset:self.offset + self.limit] def get_paginated_response(self, data): @@ -285,16 +322,45 @@ class LimitOffsetPagination(BasePagination): except (KeyError, ValueError): return 0 - def get_next_link(self, page): + def get_next_link(self): if self.offset + self.limit >= self.count: return None + url = self.request.build_absolute_uri() offset = self.offset + self.limit return replace_query_param(url, self.offset_query_param, offset) - def get_previous_link(self, page): - if self.offset - self.limit < 0: + def get_previous_link(self): + if self.offset <= 0: return None + url = self.request.build_absolute_uri() + + if self.offset - self.limit <= 0: + return remove_query_param(url, self.offset_query_param) + offset = self.offset - self.limit return replace_query_param(url, self.offset_query_param, offset) + + def to_html(self): + base_url = self.request.build_absolute_uri() + current = _divide_with_ceil(self.offset, self.limit) + 1 + final = _divide_with_ceil(self.count, self.limit) + + def page_number_to_url(page_number): + if page_number == 1: + return remove_query_param(base_url, self.offset_query_param) + else: + offset = self.offset + ((page_number - current) * self.limit) + return replace_query_param(base_url, self.offset_query_param, offset) + + page_numbers = _get_displayed_page_numbers(current, final) + page_links = _get_page_links(page_numbers, current, page_number_to_url) + + template = loader.get_template(self.template) + context = Context({ + 'previous_url': self.get_previous_link(), + 'next_url': self.get_next_link(), + 'page_links': page_links + }) + return template.render(context) \ No newline at end of file diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 4c002b16..4c46b049 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -584,6 +584,11 @@ class BrowsableAPIRenderer(BaseRenderer): renderer_content_type += ' ;%s' % renderer.charset response_headers['Content-Type'] = renderer_content_type + if hasattr(view, 'paginator') and view.paginator.display_page_controls: + paginator = view.paginator + else: + paginator = None + context = { 'content': self.get_content(renderer, data, accepted_media_type, renderer_context), 'view': view, @@ -592,7 +597,7 @@ class BrowsableAPIRenderer(BaseRenderer): 'description': self.get_description(view), 'name': self.get_name(view), 'version': VERSION, - 'pager': getattr(view, 'pager', None), + 'paginator': paginator, 'breadcrumblist': self.get_breadcrumbs(request), 'allowed_methods': view.allowed_methods, 'available_formats': [renderer_cls.format for renderer_cls in view.renderer_classes], diff --git a/rest_framework/static/rest_framework/css/bootstrap-tweaks.css b/rest_framework/static/rest_framework/css/bootstrap-tweaks.css index d4a7d31a..15b42178 100644 --- a/rest_framework/static/rest_framework/css/bootstrap-tweaks.css +++ b/rest_framework/static/rest_framework/css/bootstrap-tweaks.css @@ -60,6 +60,13 @@ a single block in the template. color: #C20000; } +.pagination>.disabled>a, +.pagination>.disabled>a:hover, +.pagination>.disabled>a:focus { + cursor: default; + pointer-events: none; +} + /*=== dabapps bootstrap styles ====*/ html { diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html index e0030981..877387f2 100644 --- a/rest_framework/templates/rest_framework/base.html +++ b/rest_framework/templates/rest_framework/base.html @@ -125,9 +125,9 @@ {% endblock %} - {% if pager.mark_as_used %} + {% if paginator %} {% endif %} -- cgit v1.2.3 From 68dfa369b5ca877643b41c8df7c5fc0c786a9f08 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 15 Jan 2015 16:55:04 +0000 Subject: Flake 8 fixes --- rest_framework/pagination.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 2b78f1f7..61b8e07a 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -251,6 +251,7 @@ class PageNumberPagination(BasePagination): def to_html(self): base_url = self.request.build_absolute_uri() + def page_number_to_url(page_number): if page_number == 1: return remove_query_param(base_url, self.page_query_param) @@ -363,4 +364,4 @@ class LimitOffsetPagination(BasePagination): 'next_url': self.get_next_link(), 'page_links': page_links }) - return template.render(context) \ No newline at end of file + return template.render(context) -- cgit v1.2.3 From 53edd37df5aa0ac29dbe7824db2e33da1d901f98 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 15 Jan 2015 21:07:05 +0000 Subject: Tests for LimitOffsetPagination --- rest_framework/pagination.py | 57 +++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 30 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 61b8e07a..0dac5683 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -44,7 +44,7 @@ def _get_count(queryset): """ try: return queryset.count() - except AttributeError: + except (AttributeError, TypeError): return len(queryset) @@ -111,12 +111,7 @@ def _get_page_links(page_numbers, current, url_func): page_links = [] for page_number in page_numbers: if page_number is None: - page_link = PageLink( - url=None, - number=None, - is_active=False, - is_break=True - ) + page_link = PAGE_BREAK else: page_link = PageLink( url=url_func(page_number), @@ -130,11 +125,13 @@ def _get_page_links(page_numbers, current, url_func): PageLink = namedtuple('PageLink', ['url', 'number', 'is_active', 'is_break']) +PAGE_BREAK = PageLink(url=None, number=None, is_active=False, is_break=True) + class BasePagination(object): display_page_controls = False - def paginate_queryset(self, queryset, request, view): + def paginate_queryset(self, queryset, request, view=None): raise NotImplemented('paginate_queryset() must be implemented.') def get_paginated_response(self, data): @@ -167,9 +164,11 @@ class PageNumberPagination(BasePagination): # Only relevant if 'paginate_by_param' has also been set. max_paginate_by = api_settings.MAX_PAGINATE_BY + last_page_strings = ('last',) + template = 'rest_framework/pagination/numbers.html' - def paginate_queryset(self, queryset, request, view): + def paginate_queryset(self, queryset, request, view=None): """ Paginate a queryset if required, either returning a page object, or `None` if pagination is not configured for this view. @@ -186,18 +185,9 @@ class PageNumberPagination(BasePagination): return None paginator = DjangoPaginator(queryset, page_size) - page_string = request.query_params.get(self.page_query_param, 1) - try: - page_number = paginator.validate_number(page_string) - except InvalidPage: - if page_string == 'last': - page_number = paginator.num_pages - else: - msg = _( - 'Choose a valid page number. Page numbers must be a ' - 'whole number, or must be the string "last".' - ) - raise NotFound(msg) + page_number = request.query_params.get(self.page_query_param, 1) + if page_number in self.last_page_strings: + page_number = paginator.num_pages try: self.page = paginator.page(page_number) @@ -210,6 +200,7 @@ class PageNumberPagination(BasePagination): if paginator.count > 1: # The browsable API should display pagination controls. self.display_page_controls = True + self.request = request return self.page @@ -249,7 +240,7 @@ class PageNumberPagination(BasePagination): return remove_query_param(url, self.page_query_param) return replace_query_param(url, self.page_query_param, page_number) - def to_html(self): + def get_html_context(self): base_url = self.request.build_absolute_uri() def page_number_to_url(page_number): @@ -263,12 +254,15 @@ class PageNumberPagination(BasePagination): page_numbers = _get_displayed_page_numbers(current, final) page_links = _get_page_links(page_numbers, current, page_number_to_url) - template = loader.get_template(self.template) - context = Context({ + return { 'previous_url': self.get_previous_link(), 'next_url': self.get_next_link(), 'page_links': page_links - }) + } + + def to_html(self): + template = loader.get_template(self.template) + context = Context(self.get_html_context()) return template.render(context) @@ -286,7 +280,7 @@ class LimitOffsetPagination(BasePagination): template = 'rest_framework/pagination/numbers.html' - def paginate_queryset(self, queryset, request, view): + def paginate_queryset(self, queryset, request, view=None): self.limit = self.get_limit(request) self.offset = self.get_offset(request) self.count = _get_count(queryset) @@ -343,7 +337,7 @@ class LimitOffsetPagination(BasePagination): offset = self.offset - self.limit return replace_query_param(url, self.offset_query_param, offset) - def to_html(self): + def get_html_context(self): base_url = self.request.build_absolute_uri() current = _divide_with_ceil(self.offset, self.limit) + 1 final = _divide_with_ceil(self.count, self.limit) @@ -358,10 +352,13 @@ class LimitOffsetPagination(BasePagination): page_numbers = _get_displayed_page_numbers(current, final) page_links = _get_page_links(page_numbers, current, page_number_to_url) - template = loader.get_template(self.template) - context = Context({ + return { 'previous_url': self.get_previous_link(), 'next_url': self.get_next_link(), 'page_links': page_links - }) + } + + def to_html(self): + template = loader.get_template(self.template) + context = Context(self.get_html_context()) return template.render(context) -- cgit v1.2.3 From 8b0f25aa0a91cb7b56f9ce4dde4330fe5daaad9b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 16 Jan 2015 16:55:46 +0000 Subject: More pagination tests & cleanup --- rest_framework/generics.py | 12 +++++++++++- rest_framework/pagination.py | 31 +++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 9 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/generics.py b/rest_framework/generics.py index 4cc4c64d..61dcb84a 100644 --- a/rest_framework/generics.py +++ b/rest_framework/generics.py @@ -151,6 +151,9 @@ class GenericAPIView(views.APIView): @property def paginator(self): + """ + The paginator instance associated with the view, or `None`. + """ if not hasattr(self, '_paginator'): if self.pagination_class is None: self._paginator = None @@ -159,11 +162,18 @@ class GenericAPIView(views.APIView): return self._paginator def paginate_queryset(self, queryset): + """ + Return a single page of results, or `None` if pagination is disabled. + """ if self.paginator is None: - return queryset + return None return self.paginator.paginate_queryset(queryset, self.request, view=self) def get_paginated_response(self, data): + """ + Return a paginated style `Response` object for the given output data. + """ + assert self.paginator is not None return self.paginator.get_paginated_response(data) diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 0dac5683..c5a364f0 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -131,13 +131,13 @@ PAGE_BREAK = PageLink(url=None, number=None, is_active=False, is_break=True) class BasePagination(object): display_page_controls = False - def paginate_queryset(self, queryset, request, view=None): + def paginate_queryset(self, queryset, request, view=None): # pragma: no cover raise NotImplemented('paginate_queryset() must be implemented.') - def get_paginated_response(self, data): + def get_paginated_response(self, data): # pragma: no cover raise NotImplemented('get_paginated_response() must be implemented.') - def to_html(self): + def to_html(self): # pragma: no cover raise NotImplemented('to_html() must be implemented to display page controls.') @@ -168,10 +168,11 @@ class PageNumberPagination(BasePagination): template = 'rest_framework/pagination/numbers.html' - def paginate_queryset(self, queryset, request, view=None): + def _handle_backwards_compat(self, view): """ - Paginate a queryset if required, either returning a - page object, or `None` if pagination is not configured for this view. + Prior to version 3.1, pagination was handled in the view, and the + attributes were set there. The attributes should now be set on + the pagination class, but the old style is still pending deprecation. """ for attr in ( 'paginate_by', 'page_query_param', @@ -180,6 +181,13 @@ class PageNumberPagination(BasePagination): if hasattr(view, attr): setattr(self, attr, getattr(view, attr)) + def paginate_queryset(self, queryset, request, view=None): + """ + Paginate a queryset if required, either returning a + page object, or `None` if pagination is not configured for this view. + """ + self._handle_backwards_compat(view) + page_size = self.get_page_size(request) if not page_size: return None @@ -277,7 +285,6 @@ class LimitOffsetPagination(BasePagination): limit_query_param = 'limit' offset_query_param = 'offset' max_limit = None - template = 'rest_framework/pagination/numbers.html' def paginate_queryset(self, queryset, request, view=None): @@ -340,7 +347,15 @@ class LimitOffsetPagination(BasePagination): def get_html_context(self): base_url = self.request.build_absolute_uri() current = _divide_with_ceil(self.offset, self.limit) + 1 - final = _divide_with_ceil(self.count, self.limit) + # The number of pages is a little bit fiddly. + # We need to sum both the number of pages from current offset to end + # plus the number of pages up to the current offset. + # When offset is not strictly divisible by the limit then we may + # end up introducing an extra page as an artifact. + final = ( + _divide_with_ceil(self.count - self.offset, self.limit) + + _divide_with_ceil(self.offset, self.limit) + ) def page_number_to_url(page_number): if page_number == 1: -- cgit v1.2.3 From 86d2774cf30351fd4174e97501532056ed0d8f95 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 16 Jan 2015 20:30:46 +0000 Subject: Fix compat issues --- rest_framework/pagination.py | 8 +++---- rest_framework/templatetags/rest_framework.py | 34 +++------------------------ rest_framework/utils/urls.py | 25 ++++++++++++++++++++ 3 files changed, 32 insertions(+), 35 deletions(-) create mode 100644 rest_framework/utils/urls.py (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index c5a364f0..1b7524c6 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -12,7 +12,7 @@ from rest_framework.compat import OrderedDict from rest_framework.exceptions import NotFound from rest_framework.response import Response from rest_framework.settings import api_settings -from rest_framework.templatetags.rest_framework import ( +from rest_framework.utils.urls import ( replace_query_param, remove_query_param ) @@ -34,8 +34,8 @@ def _divide_with_ceil(a, b): Returns 'a' divded by 'b', with any remainder rounded up. """ if a % b: - return (a / b) + 1 - return a / b + return (a // b) + 1 + return a // b def _get_count(queryset): @@ -70,7 +70,7 @@ def _get_displayed_page_numbers(current, final): assert final >= current if final <= 5: - return range(1, final + 1) + return list(range(1, final + 1)) # We always include the first two pages, last two pages, and # two pages either side of the current page. diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index bf159d8b..a969836f 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -1,41 +1,19 @@ from __future__ import unicode_literals, absolute_import from django import template from django.core.urlresolvers import reverse, NoReverseMatch -from django.http import QueryDict from django.utils import six -from django.utils.six.moves.urllib import parse as urlparse from django.utils.encoding import iri_to_uri, force_text from django.utils.html import escape from django.utils.safestring import SafeData, mark_safe from django.utils.html import smart_urlquote from rest_framework.renderers import HTMLFormRenderer +from rest_framework.utils.urls import replace_query_param import re register = template.Library() - -def replace_query_param(url, key, val): - """ - Given a URL and a key/val pair, set or replace an item in the query - parameters of the URL, and return the new URL. - """ - (scheme, netloc, path, query, fragment) = urlparse.urlsplit(url) - query_dict = QueryDict(query).copy() - query_dict[key] = val - query = query_dict.urlencode() - return urlparse.urlunsplit((scheme, netloc, path, query, fragment)) - - -def remove_query_param(url, key): - """ - Given a URL and a key/val pair, set or replace an item in the query - parameters of the URL, and return the new URL. - """ - (scheme, netloc, path, query, fragment) = urlparse.urlsplit(url) - query_dict = QueryDict(query).copy() - query_dict.pop(key, None) - query = query_dict.urlencode() - return urlparse.urlunsplit((scheme, netloc, path, query, fragment)) +# Regex for adding classes to html snippets +class_re = re.compile(r'(?<=class=["\'])(.*)(?=["\'])') @register.simple_tag @@ -43,12 +21,6 @@ def get_pagination_html(pager): return pager.to_html() -# Regex for adding classes to html snippets -class_re = re.compile(r'(?<=class=["\'])(.*)(?=["\'])') - - -# And the template tags themselves... - @register.simple_tag def render_field(field, style=None): style = style or {} diff --git a/rest_framework/utils/urls.py b/rest_framework/utils/urls.py new file mode 100644 index 00000000..880ef9ed --- /dev/null +++ b/rest_framework/utils/urls.py @@ -0,0 +1,25 @@ +from django.utils.six.moves.urllib import parse as urlparse + + +def replace_query_param(url, key, val): + """ + Given a URL and a key/val pair, set or replace an item in the query + parameters of the URL, and return the new URL. + """ + (scheme, netloc, path, query, fragment) = urlparse.urlsplit(url) + query_dict = urlparse.parse_qs(query) + query_dict[key] = [val] + query = urlparse.urlencode(sorted(list(query_dict.items())), doseq=True) + return urlparse.urlunsplit((scheme, netloc, path, query, fragment)) + + +def remove_query_param(url, key): + """ + Given a URL and a key/val pair, remove an item in the query + parameters of the URL, and return the new URL. + """ + (scheme, netloc, path, query, fragment) = urlparse.urlsplit(url) + query_dict = urlparse.parse_qs(query) + query_dict.pop(key, None) + query = urlparse.urlencode(sorted(list(query_dict.items())), doseq=True) + return urlparse.urlunsplit((scheme, netloc, path, query, fragment)) -- cgit v1.2.3 From 4919492582547d227a22852ad2339fa73739cc94 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sat, 17 Jan 2015 00:10:43 +0000 Subject: First pass at cursor pagination --- rest_framework/pagination.py | 51 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 1b7524c6..89d6f9f4 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -3,10 +3,12 @@ Pagination serializers determine the structure of the output that should be used for paginated responses. """ from __future__ import unicode_literals +from base64 import b64encode, b64decode from collections import namedtuple from django.core.paginator import InvalidPage, Paginator as DjangoPaginator from django.template import Context, loader from django.utils import six +from django.utils.six.moves.urllib import parse as urlparse from django.utils.translation import ugettext as _ from rest_framework.compat import OrderedDict from rest_framework.exceptions import NotFound @@ -377,3 +379,52 @@ class LimitOffsetPagination(BasePagination): template = loader.get_template(self.template) context = Context(self.get_html_context()) return template.render(context) + + +class CursorPagination(BasePagination): + # reverse + # limit + # multiple orderings + cursor_query_param = 'cursor' + page_size = 5 + + def paginate_queryset(self, queryset, request, view=None): + self.base_url = request.build_absolute_uri() + self.ordering = self.get_ordering() + encoded = request.query_params.get(self.cursor_query_param) + + if encoded is None: + cursor = None + else: + cursor = self.decode_cursor(encoded, self.ordering) + + if cursor is not None: + kwargs = {self.ordering + '__gt': cursor} + queryset = queryset.filter(**kwargs) + + results = list(queryset[:self.page_size + 1]) + self.page = results[:self.page_size] + self.has_next = len(results) > len(self.page) + return self.page + + def get_next_link(self): + if not self.has_next: + return None + last_item = self.page[-1] + cursor = self.get_cursor_from_instance(last_item, self.ordering) + encoded = self.encode_cursor(cursor, self.ordering) + return replace_query_param(self.base_url, self.cursor_query_param, encoded) + + def get_ordering(self): + return 'created' + + def get_cursor_from_instance(self, instance, ordering): + return getattr(instance, ordering) + + def decode_cursor(self, encoded, ordering): + items = urlparse.parse_qs(b64decode(encoded)) + return items.get(ordering)[0] + + def encode_cursor(self, cursor, ordering): + items = [(ordering, cursor)] + return b64encode(urlparse.urlencode(items, doseq=True)) -- cgit v1.2.3 From 492f3c410d3a91a3f37218e93485a693d9078000 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sat, 17 Jan 2015 00:59:02 +0000 Subject: Cleaning up cursor implementation --- rest_framework/pagination.py | 51 +++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 13 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 89d6f9f4..3984da13 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -381,10 +381,33 @@ class LimitOffsetPagination(BasePagination): return template.render(context) +Cursor = namedtuple('Cursor', ['offset', 'reverse', 'position']) + + +def decode_cursor(encoded): + tokens = urlparse.parse_qs(b64decode(encoded)) + try: + offset = int(tokens['offset'][0]) + reverse = bool(int(tokens['reverse'][0])) + position = tokens['position'][0] + except (TypeError, ValueError): + return None + + return Cursor(offset=offset, reverse=reverse, position=position) + + +def encode_cursor(cursor): + tokens = { + 'offset': str(cursor.offset), + 'reverse': '1' if cursor.reverse else '0', + 'position': cursor.position + } + return b64encode(urlparse.urlencode(tokens, doseq=True)) + + class CursorPagination(BasePagination): # reverse # limit - # multiple orderings cursor_query_param = 'cursor' page_size = 5 @@ -396,10 +419,11 @@ class CursorPagination(BasePagination): if encoded is None: cursor = None else: - cursor = self.decode_cursor(encoded, self.ordering) + cursor = decode_cursor(encoded) + # TODO: Invalid cursors should 404 if cursor is not None: - kwargs = {self.ordering + '__gt': cursor} + kwargs = {self.ordering + '__gt': cursor.position} queryset = queryset.filter(**kwargs) results = list(queryset[:self.page_size + 1]) @@ -411,20 +435,21 @@ class CursorPagination(BasePagination): if not self.has_next: return None last_item = self.page[-1] - cursor = self.get_cursor_from_instance(last_item, self.ordering) - encoded = self.encode_cursor(cursor, self.ordering) + position = self.get_position_from_instance(last_item, self.ordering) + cursor = Cursor(offset=0, reverse=False, position=position) + encoded = encode_cursor(cursor) return replace_query_param(self.base_url, self.cursor_query_param, encoded) def get_ordering(self): return 'created' - def get_cursor_from_instance(self, instance, ordering): - return getattr(instance, ordering) + def get_position_from_instance(self, instance, ordering): + return str(getattr(instance, ordering)) - def decode_cursor(self, encoded, ordering): - items = urlparse.parse_qs(b64decode(encoded)) - return items.get(ordering)[0] + # def decode_cursor(self, encoded, ordering): + # items = urlparse.parse_qs(b64decode(encoded)) + # return items.get(ordering)[0] - def encode_cursor(self, cursor, ordering): - items = [(ordering, cursor)] - return b64encode(urlparse.urlencode(items, doseq=True)) + # def encode_cursor(self, cursor, ordering): + # items = [(ordering, cursor)] + # return b64encode(urlparse.urlencode(items, doseq=True)) -- cgit v1.2.3 From dbb684117f6fe0f9c34f98d5e914fc106090cdbc Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 19 Jan 2015 09:24:42 +0000 Subject: Add offset support for cursor pagination --- rest_framework/pagination.py | 67 +++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 19 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 3984da13..f56f55ce 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -1,3 +1,4 @@ +# coding: utf-8 """ Pagination serializers determine the structure of the output that should be used for paginated responses. @@ -385,7 +386,7 @@ Cursor = namedtuple('Cursor', ['offset', 'reverse', 'position']) def decode_cursor(encoded): - tokens = urlparse.parse_qs(b64decode(encoded)) + tokens = urlparse.parse_qs(b64decode(encoded), keep_blank_values=True) try: offset = int(tokens['offset'][0]) reverse = bool(int(tokens['reverse'][0])) @@ -406,8 +407,7 @@ def encode_cursor(cursor): class CursorPagination(BasePagination): - # reverse - # limit + # TODO: reverse cursors cursor_query_param = 'cursor' page_size = 5 @@ -417,26 +417,63 @@ class CursorPagination(BasePagination): encoded = request.query_params.get(self.cursor_query_param) if encoded is None: - cursor = None + self.cursor = None else: - cursor = decode_cursor(encoded) + self.cursor = decode_cursor(encoded) # TODO: Invalid cursors should 404 - if cursor is not None: - kwargs = {self.ordering + '__gt': cursor.position} + if self.cursor is not None and self.cursor.position != '': + kwargs = {self.ordering + '__gt': self.cursor.position} queryset = queryset.filter(**kwargs) - results = list(queryset[:self.page_size + 1]) + # The offset is used in order to deal with cases where we have + # items with an identical position. This allows the cursors + # to gracefully deal with non-unique fields as the ordering. + offset = 0 if (self.cursor is None) else self.cursor.offset + + # We fetch an extra item in order to determine if there is a next page. + results = list(queryset[offset:offset + self.page_size + 1]) self.page = results[:self.page_size] self.has_next = len(results) > len(self.page) + self.next_item = results[-1] if self.has_next else None return self.page def get_next_link(self): if not self.has_next: return None - last_item = self.page[-1] - position = self.get_position_from_instance(last_item, self.ordering) - cursor = Cursor(offset=0, reverse=False, position=position) + + compare = self.get_position_from_instance(self.next_item, self.ordering) + offset = 0 + for item in reversed(self.page): + position = self.get_position_from_instance(item, self.ordering) + if position != compare: + # The item in this position and the item following it + # have different positions. We can use this position as + # our marker. + break + + # The item in this postion has the same position as the item + # following it, we can't use it as a marker position, so increment + # the offset and keep seeking to the previous item. + compare = position + offset += 1 + + else: + if self.cursor is None: + # There were no unique positions in the page, and we were + # on the first page, ie. there was no existing cursor. + # Our cursor will have an offset equal to the page size, + # but no position to filter against yet. + offset = self.page_size + position = '' + else: + # There were no unique positions in the page. + # Use the position from the existing cursor and increment + # it's offset by the page size. + offset = self.cursor.offset + self.page_size + position = self.cursor.position + + cursor = Cursor(offset=offset, reverse=False, position=position) encoded = encode_cursor(cursor) return replace_query_param(self.base_url, self.cursor_query_param, encoded) @@ -445,11 +482,3 @@ class CursorPagination(BasePagination): def get_position_from_instance(self, instance, ordering): return str(getattr(instance, ordering)) - - # def decode_cursor(self, encoded, ordering): - # items = urlparse.parse_qs(b64decode(encoded)) - # return items.get(ordering)[0] - - # def encode_cursor(self, cursor, ordering): - # items = [(ordering, cursor)] - # return b64encode(urlparse.urlencode(items, doseq=True)) -- cgit v1.2.3 From 5484d570cb8214c776273b45320e7d1c73b85a34 Mon Sep 17 00:00:00 2001 From: Fabien Bochu Date: Mon, 19 Jan 2015 10:57:26 +0100 Subject: Fix timedelta JSON serialization on Python 2.6. --- rest_framework/compat.py | 8 ++++++++ rest_framework/utils/encoders.py | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 971dee9c..17814136 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -33,6 +33,14 @@ def unicode_to_repr(value): return value +def total_seconds(timedelta): + # TimeDelta.total_seconds() is only available in Python 2.7 + if hasattr(timedelta, 'total_seconds'): + return timedelta.total_seconds() + else: + return (timedelta.days * 86400.0) + float(timedelta.seconds) + (timedelta.microseconds / 1000000.0) + + # OrderedDict only available in Python 2.7. # This will always be the case in Django 1.7 and above, as these versions # no longer support Python 2.6. diff --git a/rest_framework/utils/encoders.py b/rest_framework/utils/encoders.py index 73cbe5d8..104343a4 100644 --- a/rest_framework/utils/encoders.py +++ b/rest_framework/utils/encoders.py @@ -6,7 +6,7 @@ from django.db.models.query import QuerySet from django.utils import six, timezone from django.utils.encoding import force_text from django.utils.functional import Promise -from rest_framework.compat import OrderedDict +from rest_framework.compat import OrderedDict, total_seconds from rest_framework.utils.serializer_helpers import ReturnDict, ReturnList import datetime import decimal @@ -41,7 +41,7 @@ class JSONEncoder(json.JSONEncoder): representation = representation[:12] return representation elif isinstance(obj, datetime.timedelta): - return six.text_type(obj.total_seconds()) + return six.text_type(total_seconds(obj)) elif isinstance(obj, decimal.Decimal): # Serializers will coerce decimals to strings by default. return float(obj) -- cgit v1.2.3 From da1db34a36f6f0cb8722acbbf7c3182e32995ae3 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 19 Jan 2015 14:18:02 +0000 Subject: Handle UUID objects in JSONEncoder. Closes #2433. --- rest_framework/utils/encoders.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'rest_framework') diff --git a/rest_framework/utils/encoders.py b/rest_framework/utils/encoders.py index 104343a4..bf753271 100644 --- a/rest_framework/utils/encoders.py +++ b/rest_framework/utils/encoders.py @@ -12,6 +12,7 @@ import datetime import decimal import types import json +import uuid class JSONEncoder(json.JSONEncoder): @@ -45,6 +46,8 @@ class JSONEncoder(json.JSONEncoder): elif isinstance(obj, decimal.Decimal): # Serializers will coerce decimals to strings by default. return float(obj) + elif isinstance(obj, uuid.UUID): + return six.text_type(obj) elif isinstance(obj, QuerySet): return tuple(obj) elif hasattr(obj, 'tolist'): -- cgit v1.2.3 From 0dffc46cb71ec868cdbb37187ba03dea7bac4e7a Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 19 Jan 2015 14:21:09 +0000 Subject: ReturnDict and ReturnList repr as standard dict/list. Closes #2421. --- rest_framework/utils/serializer_helpers.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'rest_framework') diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py index 65a04d06..f9960603 100644 --- a/rest_framework/utils/serializer_helpers.py +++ b/rest_framework/utils/serializer_helpers.py @@ -16,6 +16,9 @@ class ReturnDict(OrderedDict): def copy(self): return ReturnDict(self, serializer=self.serializer) + def __repr__(self): + return dict.__repr__(self) + class ReturnList(list): """ @@ -27,6 +30,9 @@ class ReturnList(list): self.serializer = kwargs.pop('serializer') super(ReturnList, self).__init__(*args, **kwargs) + def __repr__(self): + return list.__repr__(self) + class BoundField(object): """ -- cgit v1.2.3 From af05820b1bfc60ceb7cbb35566588a732938cae2 Mon Sep 17 00:00:00 2001 From: Alexander Dutton Date: Mon, 19 Jan 2015 14:23:13 +0000 Subject: NotImplemented is not an exception `NotImplemented` is a singleton object, not an exception. You should be raising `NotImplementedError` here instead.--- rest_framework/renderers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 634338e9..ba6c9cc1 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -46,7 +46,7 @@ class BaseRenderer(object): render_style = 'text' def render(self, data, accepted_media_type=None, renderer_context=None): - raise NotImplemented('Renderer class requires .render() to be implemented') + raise NotImplementedError('Renderer class requires .render() to be implemented') class JSONRenderer(BaseRenderer): -- cgit v1.2.3 From 4f3c3a06cfc0ea2dfbf46da2d98546664343ce93 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 19 Jan 2015 14:41:10 +0000 Subject: Drop trailing whitespace on indented JSON output. Closes #2429. --- rest_framework/compat.py | 2 ++ rest_framework/renderers.py | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 7241da27..ea342994 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -227,6 +227,8 @@ except ImportError: if six.PY3: SHORT_SEPARATORS = (',', ':') LONG_SEPARATORS = (', ', ': ') + INDENT_SEPARATORS = (',', ': ') else: SHORT_SEPARATORS = (b',', b':') LONG_SEPARATORS = (b', ', b': ') + INDENT_SEPARATORS = (b',', b': ') diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 4c46b049..7af03c67 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -18,7 +18,7 @@ from django.template import Context, RequestContext, loader, Template from django.test.client import encode_multipart from django.utils import six from rest_framework import exceptions, serializers, status, VERSION -from rest_framework.compat import SHORT_SEPARATORS, LONG_SEPARATORS +from rest_framework.compat import SHORT_SEPARATORS, LONG_SEPARATORS, INDENT_SEPARATORS from rest_framework.exceptions import ParseError from rest_framework.settings import api_settings from rest_framework.request import is_form_media_type, override_method @@ -87,7 +87,11 @@ class JSONRenderer(BaseRenderer): renderer_context = renderer_context or {} indent = self.get_indent(accepted_media_type, renderer_context) - separators = SHORT_SEPARATORS if (indent is None and self.compact) else LONG_SEPARATORS + + if indent is None: + separators = SHORT_SEPARATORS if self.compact else LONG_SEPARATORS + else: + separators = INDENT_SEPARATORS ret = json.dumps( data, cls=self.encoder_class, -- cgit v1.2.3 From 46a3eda08d519c6908ecd1b9c2e13e5fdb773b0c Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 19 Jan 2015 14:48:13 +0000 Subject: NotImplemented -> NotImplementedError --- rest_framework/routers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 61f3ccab..827da034 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -65,13 +65,13 @@ class BaseRouter(object): If `base_name` is not specified, attempt to automatically determine it from the viewset. """ - raise NotImplemented('get_default_base_name must be overridden') + raise NotImplementedError('get_default_base_name must be overridden') def get_urls(self): """ Return a list of URL patterns, given the registered viewsets. """ - raise NotImplemented('get_urls must be overridden') + raise NotImplementedError('get_urls must be overridden') @property def urls(self): -- cgit v1.2.3 From 3cc39ffbceffc5fdbb511d9a10e7732329e8baa4 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 19 Jan 2015 15:22:38 +0000 Subject: NotImplemented -> NotImplementedError --- rest_framework/pagination.py | 6 +++--- rest_framework/versioning.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 1b7524c6..55c173df 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -132,13 +132,13 @@ class BasePagination(object): display_page_controls = False def paginate_queryset(self, queryset, request, view=None): # pragma: no cover - raise NotImplemented('paginate_queryset() must be implemented.') + raise NotImplementedError('paginate_queryset() must be implemented.') def get_paginated_response(self, data): # pragma: no cover - raise NotImplemented('get_paginated_response() must be implemented.') + raise NotImplementedError('get_paginated_response() must be implemented.') def to_html(self): # pragma: no cover - raise NotImplemented('to_html() must be implemented to display page controls.') + raise NotImplementedError('to_html() must be implemented to display page controls.') class PageNumberPagination(BasePagination): diff --git a/rest_framework/versioning.py b/rest_framework/versioning.py index e31c71e9..a07b629f 100644 --- a/rest_framework/versioning.py +++ b/rest_framework/versioning.py @@ -17,7 +17,7 @@ class BaseVersioning(object): def determine_version(self, request, *args, **kwargs): msg = '{cls}.determine_version() must be implemented.' - raise NotImplemented(msg.format( + raise NotImplementedError(msg.format( cls=self.__class__.__name__ )) -- cgit v1.2.3 From da6ef3d0b0f3a8e688524bbd446d4350a74fd05a Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 21 Jan 2015 13:03:37 +0000 Subject: Allow missing fields option for inherited serializers. Closes #2388. --- rest_framework/compat.py | 2 +- rest_framework/serializers.py | 32 ++++++++++++++++++------------ rest_framework/utils/serializer_helpers.py | 3 +++ 3 files changed, 23 insertions(+), 14 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 17814136..766afaec 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -20,7 +20,7 @@ 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). if six.PY2: - repr(instance).decode('utf-8') + return repr(instance).decode('utf-8') return repr(instance) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index e373cd10..6320a075 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -253,7 +253,7 @@ class SerializerMetaclass(type): # If this class is subclassing another Serializer, add that Serializer's # fields. Note that we loop over the bases in *reverse*. This is necessary # in order to maintain the correct order of fields. - for base in bases[::-1]: + for base in reversed(bases): if hasattr(base, '_declared_fields'): fields = list(base._declared_fields.items()) + fields @@ -880,8 +880,8 @@ class ModelSerializer(Serializer): # Retrieve metadata about fields & relationships on the model class. info = model_meta.get_field_info(model) - # Use the default set of field names if none is supplied explicitly. if fields is None: + # Use the default set of field names if none is supplied explicitly. fields = self._get_default_field_names(declared_fields, info) exclude = getattr(self.Meta, 'exclude', None) if exclude is not None: @@ -891,6 +891,23 @@ class ModelSerializer(Serializer): field_name ) fields.remove(field_name) + else: + # Check that any fields declared on the class are + # also explicitly included in `Meta.fields`. + + # Note that we ignore any fields that were declared on a parent + # class, in order to support only including a subset of fields + # when subclassing serializers. + declared_field_names = set(declared_fields.keys()) + for cls in self.__class__.__bases__: + declared_field_names -= set(getattr(cls, '_declared_fields', [])) + + missing_fields = declared_field_names - set(fields) + assert not missing_fields, ( + 'Field `%s` has been declared on serializer `%s`, but ' + 'is missing from `Meta.fields`.' % + (list(missing_fields)[0], self.__class__.__name__) + ) # Determine the set of model fields, and the fields that they map to. # We actually only need this to deal with the slightly awkward case @@ -1024,17 +1041,6 @@ class ModelSerializer(Serializer): (field_name, model.__class__.__name__) ) - # Check that any fields declared on the class are - # also explicitly included in `Meta.fields`. - missing_fields = set(declared_fields.keys()) - set(fields) - if missing_fields: - missing_field = list(missing_fields)[0] - raise ImproperlyConfigured( - 'Field `%s` has been declared on serializer `%s`, but ' - 'is missing from `Meta.fields`.' % - (missing_field, self.__class__.__name__) - ) - # Populate any kwargs defined in `Meta.extra_kwargs` extras = extra_kwargs.get(field_name, {}) if extras.get('read_only', False): diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py index f9960603..ab057862 100644 --- a/rest_framework/utils/serializer_helpers.py +++ b/rest_framework/utils/serializer_helpers.py @@ -105,3 +105,6 @@ class BindingDict(collections.MutableMapping): def __len__(self): return len(self.fields) + + def __repr__(self): + return dict.__repr__(self.fields) -- cgit v1.2.3 From e59b3d1718de549d0e165d03aeea1488ddfe20ee Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 21 Jan 2015 14:18:13 +0000 Subject: Make ReturnDict cachable. Closes #2360. --- rest_framework/utils/serializer_helpers.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'rest_framework') diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py index ab057862..87bb3ac0 100644 --- a/rest_framework/utils/serializer_helpers.py +++ b/rest_framework/utils/serializer_helpers.py @@ -19,6 +19,11 @@ class ReturnDict(OrderedDict): def __repr__(self): return dict.__repr__(self) + def __reduce__(self): + # Pickling these objects will drop the .serializer backlink, + # but preserve the raw data. + return (dict, (dict(self),)) + class ReturnList(list): """ @@ -33,6 +38,11 @@ class ReturnList(list): def __repr__(self): return list.__repr__(self) + def __reduce__(self): + # Pickling these objects will drop the .serializer backlink, + # but preserve the raw data. + return (list, (list(self),)) + class BoundField(object): """ -- cgit v1.2.3 From 4cf03e30ff765dda2899048725da4d85ebd8af52 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 21 Jan 2015 14:26:25 +0000 Subject: Do not render HTML output for hidden fields. Closes #2410. --- rest_framework/renderers.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'rest_framework') diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index ba6c9cc1..584332e6 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -410,6 +410,9 @@ class HTMLFormRenderer(BaseRenderer): }) def render_field(self, field, parent_style): + if isinstance(field, serializers.HiddenField): + return '' + style = dict(self.default_style[field]) style.update(field.style) if 'template_pack' not in style: -- cgit v1.2.3 From 857185cf07bb539083a90bc75a6dd951da8e2206 Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Wed, 21 Jan 2015 19:29:40 +0100 Subject: Workaround Django issue 24198. --- rest_framework/serializers.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 6320a075..b1474562 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -12,7 +12,7 @@ response content is handled by parsers and renderers. """ from __future__ import unicode_literals from django.db import models -from django.db.models.fields import FieldDoesNotExist +from django.db.models.fields import FieldDoesNotExist, Field from django.utils.translation import ugettext_lazy as _ from rest_framework.compat import unicode_to_repr from rest_framework.utils import model_meta @@ -939,6 +939,9 @@ class ModelSerializer(Serializer): except FieldDoesNotExist: continue + if not isinstance(model_field, Field): + continue + # Include each of the `unique_for_*` field names. unique_constraint_names |= set([ model_field.unique_for_date, -- cgit v1.2.3 From 15f797fd3ec61947aaecc05e6fd040e1e3e8776a Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Wed, 21 Jan 2015 19:46:31 +0100 Subject: Owned by import * --- rest_framework/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index b1474562..cf797bdc 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -12,7 +12,7 @@ response content is handled by parsers and renderers. """ from __future__ import unicode_literals from django.db import models -from django.db.models.fields import FieldDoesNotExist, Field +from django.db.models.fields import FieldDoesNotExist, Field as DjangoField from django.utils.translation import ugettext_lazy as _ from rest_framework.compat import unicode_to_repr from rest_framework.utils import model_meta @@ -939,7 +939,7 @@ class ModelSerializer(Serializer): except FieldDoesNotExist: continue - if not isinstance(model_field, Field): + if not isinstance(model_field, DjangoField): continue # Include each of the `unique_for_*` field names. -- cgit v1.2.3 From cae9528c54ea13863ea056d40168e8d8df68b276 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 22 Jan 2015 10:28:19 +0000 Subject: Add support for reverse cursors --- rest_framework/pagination.py | 126 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 106 insertions(+), 20 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 5482788a..9e22a8bf 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -407,45 +407,84 @@ def encode_cursor(cursor): class CursorPagination(BasePagination): - # TODO: reverse cursors + # TODO: handle queries with '' as a legitimate position cursor_query_param = 'cursor' page_size = 5 def paginate_queryset(self, queryset, request, view=None): self.base_url = request.build_absolute_uri() self.ordering = self.get_ordering() - encoded = request.query_params.get(self.cursor_query_param) + # Determine if we have a cursor, and if so then decode it. + encoded = request.query_params.get(self.cursor_query_param) if encoded is None: self.cursor = None + (offset, reverse, current_position) = (0, False, '') else: self.cursor = decode_cursor(encoded) + (offset, reverse, current_position) = self.cursor # TODO: Invalid cursors should 404 - if self.cursor is not None and self.cursor.position != '': - kwargs = {self.ordering + '__gt': self.cursor.position} - queryset = queryset.filter(**kwargs) + # Cursor pagination always enforces an ordering. + if reverse: + queryset = queryset.order_by('-' + self.ordering) + else: + queryset = queryset.order_by(self.ordering) - # The offset is used in order to deal with cases where we have - # items with an identical position. This allows the cursors - # to gracefully deal with non-unique fields as the ordering. - offset = 0 if (self.cursor is None) else self.cursor.offset + # If we have a cursor with a fixed position then filter by that. + if current_position != '': + if self.cursor.reverse: + kwargs = {self.ordering + '__lt': current_position} + else: + kwargs = {self.ordering + '__gt': current_position} + queryset = queryset.filter(**kwargs) - # We fetch an extra item in order to determine if there is a next page. + # If we have an offset cursor then offset the entire page by that amount. + # We also always fetch an extra item in order to determine if there is a + # page following on from this one. results = list(queryset[offset:offset + self.page_size + 1]) self.page = results[:self.page_size] - self.has_next = len(results) > len(self.page) - self.next_item = results[-1] if self.has_next else None + + # Determine the position of the final item following the page. + if len(results) > len(self.page): + has_following_postion = True + following_position = self._get_position_from_instance(results[-1], self.ordering) + else: + has_following_postion = False + following_position = None + + # If we have a reverse queryset, then the query ordering was in reverse + # so we need to reverse the items again before returning them to the user. + if reverse: + self.page = reversed(self.page) + + if reverse: + # Determine next and previous positions for reverse cursors. + self.has_next = current_position != '' or offset > 0 + self.has_previous = has_following_postion + if self.has_next: + self.next_position = current_position + if self.has_previous: + self.previous_position = following_position + else: + # Determine next and previous positions for forward cursors. + self.has_next = has_following_postion + self.has_previous = current_position != '' or offset > 0 + if self.has_next: + self.next_position = following_position + if self.has_previous: + self.previous_position = current_position + return self.page def get_next_link(self): if not self.has_next: return None - compare = self.get_position_from_instance(self.next_item, self.ordering) + compare = self.next_position offset = 0 for item in reversed(self.page): - position = self.get_position_from_instance(item, self.ordering) + position = self._get_position_from_instance(item, self.ordering) if position != compare: # The item in this position and the item following it # have different positions. We can use this position as @@ -459,26 +498,73 @@ class CursorPagination(BasePagination): offset += 1 else: - if self.cursor is None: - # There were no unique positions in the page, and we were - # on the first page, ie. there was no existing cursor. + # There were no unique positions in the page. + if not self.has_previous: + # We are on the first page. # Our cursor will have an offset equal to the page size, # but no position to filter against yet. offset = self.page_size position = '' + elif self.cursor.reverse: + # The change in direction will introduce a paging artifact, + # where we end up skipping forward a few extra items. + offset = 0 + position = self.previous_position else: - # There were no unique positions in the page. # Use the position from the existing cursor and increment # it's offset by the page size. offset = self.cursor.offset + self.page_size - position = self.cursor.position + position = self.previous_position cursor = Cursor(offset=offset, reverse=False, position=position) encoded = encode_cursor(cursor) return replace_query_param(self.base_url, self.cursor_query_param, encoded) + def get_previous_link(self): + if not self.has_previous: + return None + + compare = self.previous_position + offset = 0 + for item in self.page: + position = self._get_position_from_instance(item, self.ordering) + if position != compare: + # The item in this position and the item following it + # have different positions. We can use this position as + # our marker. + break + + # The item in this postion has the same position as the item + # following it, we can't use it as a marker position, so increment + # the offset and keep seeking to the previous item. + compare = position + offset += 1 + + else: + # There were no unique positions in the page. + if not self.has_next: + # We are on the final page. + # Our cursor will have an offset equal to the page size, + # but no position to filter against yet. + offset = self.page_size + position = '' + elif self.cursor.reverse: + # Use the position from the existing cursor and increment + # it's offset by the page size. + offset = self.cursor.offset + self.page_size + position = self.next_position + else: + # The change in direction will introduce a paging artifact, + # where we end up skipping back a few extra items. + offset = 0 + position = self.next_position + + cursor = Cursor(offset=offset, reverse=True, position=position) + encoded = encode_cursor(cursor) + return replace_query_param(self.base_url, self.cursor_query_param, encoded) + def get_ordering(self): return 'created' - def get_position_from_instance(self, instance, ordering): + def _get_position_from_instance(self, instance, ordering): return str(getattr(instance, ordering)) -- cgit v1.2.3 From f1af603fb05fce236a4258e18df8af8888043247 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 22 Jan 2015 10:51:04 +0000 Subject: Tests for reverse pagination --- rest_framework/pagination.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 9e22a8bf..d5af2ac8 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -408,6 +408,8 @@ def encode_cursor(cursor): class CursorPagination(BasePagination): # TODO: handle queries with '' as a legitimate position + # Support case where ordering is already negative + # Support tuple orderings cursor_query_param = 'cursor' page_size = 5 -- cgit v1.2.3 From 94b5f7a86e401e46f14fb8982afaa7a8c61847c9 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 22 Jan 2015 12:14:52 +0000 Subject: Tidy up cursor tests and make more comprehensive --- rest_framework/pagination.py | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index d5af2ac8..61835239 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -171,6 +171,8 @@ class PageNumberPagination(BasePagination): template = 'rest_framework/pagination/numbers.html' + invalid_page_message = _('Invalid page "{page_number}": {message}.') + def _handle_backwards_compat(self, view): """ Prior to version 3.1, pagination was handled in the view, and the @@ -203,7 +205,7 @@ class PageNumberPagination(BasePagination): try: self.page = paginator.page(page_number) except InvalidPage as exc: - msg = _('Invalid page "{page_number}": {message}.').format( + msg = self.invalid_page_message.format( page_number=page_number, message=six.text_type(exc) ) raise NotFound(msg) @@ -386,8 +388,8 @@ Cursor = namedtuple('Cursor', ['offset', 'reverse', 'position']) def decode_cursor(encoded): - tokens = urlparse.parse_qs(b64decode(encoded), keep_blank_values=True) try: + tokens = urlparse.parse_qs(b64decode(encoded), keep_blank_values=True) offset = int(tokens['offset'][0]) reverse = bool(int(tokens['reverse'][0])) position = tokens['position'][0] @@ -411,7 +413,8 @@ class CursorPagination(BasePagination): # Support case where ordering is already negative # Support tuple orderings cursor_query_param = 'cursor' - page_size = 5 + page_size = api_settings.PAGINATE_BY + invalid_cursor_message = _('Invalid cursor') def paginate_queryset(self, queryset, request, view=None): self.base_url = request.build_absolute_uri() @@ -424,8 +427,9 @@ class CursorPagination(BasePagination): (offset, reverse, current_position) = (0, False, '') else: self.cursor = decode_cursor(encoded) + if self.cursor is None: + raise NotFound(self.invalid_cursor_message) (offset, reverse, current_position) = self.cursor - # TODO: Invalid cursors should 404 # Cursor pagination always enforces an ordering. if reverse: @@ -458,7 +462,7 @@ class CursorPagination(BasePagination): # If we have a reverse queryset, then the query ordering was in reverse # so we need to reverse the items again before returning them to the user. if reverse: - self.page = reversed(self.page) + self.page = list(reversed(self.page)) if reverse: # Determine next and previous positions for reverse cursors. @@ -483,8 +487,14 @@ class CursorPagination(BasePagination): if not self.has_next: return None - compare = self.next_position + if self.cursor and self.cursor.reverse and self.cursor.offset != 0: + # If we're reversing direction and we have an offset cursor + # then we cannot use the first position we find as a marker. + compare = self._get_position_from_instance(self.page[-1], self.ordering) + else: + compare = self.next_position offset = 0 + for item in reversed(self.page): position = self._get_position_from_instance(item, self.ordering) if position != compare: @@ -526,8 +536,14 @@ class CursorPagination(BasePagination): if not self.has_previous: return None - compare = self.previous_position + if self.cursor and not self.cursor.reverse and self.cursor.offset != 0: + # If we're reversing direction and we have an offset cursor + # then we cannot use the first position we find as a marker. + compare = self._get_position_from_instance(self.page[0], self.ordering) + else: + compare = self.previous_position offset = 0 + for item in self.page: position = self._get_position_from_instance(item, self.ordering) if position != compare: -- cgit v1.2.3 From ca372ef6ef1cf95eb9282a484782e1a3721cb72b Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 22 Jan 2015 13:50:51 +0000 Subject: Fix for python 3 --- rest_framework/pagination.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 61835239..0c5abccb 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -389,7 +389,7 @@ Cursor = namedtuple('Cursor', ['offset', 'reverse', 'position']) def decode_cursor(encoded): try: - tokens = urlparse.parse_qs(b64decode(encoded), keep_blank_values=True) + tokens = urlparse.parse_qs(b64decode(encoded).decode('ascii'), keep_blank_values=True) offset = int(tokens['offset'][0]) reverse = bool(int(tokens['reverse'][0])) position = tokens['position'][0] @@ -405,7 +405,7 @@ def encode_cursor(cursor): 'reverse': '1' if cursor.reverse else '0', 'position': cursor.position } - return b64encode(urlparse.urlencode(tokens, doseq=True)) + return b64encode(urlparse.urlencode(tokens, doseq=True).encode('ascii')) class CursorPagination(BasePagination): -- cgit v1.2.3 From 38a2ed6f62adcdcb2eba94f6133d4dd976a53af1 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 22 Jan 2015 14:04:25 +0000 Subject: Python 3 fixes for cursor pagination --- rest_framework/pagination.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 0c5abccb..cf1f1afa 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -389,7 +389,8 @@ Cursor = namedtuple('Cursor', ['offset', 'reverse', 'position']) def decode_cursor(encoded): try: - tokens = urlparse.parse_qs(b64decode(encoded).decode('ascii'), keep_blank_values=True) + querystring = b64decode(encoded.encode('ascii')).decode('ascii') + tokens = urlparse.parse_qs(querystring, keep_blank_values=True) offset = int(tokens['offset'][0]) reverse = bool(int(tokens['reverse'][0])) position = tokens['position'][0] @@ -405,7 +406,8 @@ def encode_cursor(cursor): 'reverse': '1' if cursor.reverse else '0', 'position': cursor.position } - return b64encode(urlparse.urlencode(tokens, doseq=True).encode('ascii')) + querystring = urlparse.urlencode(tokens, doseq=True) + return b64encode(querystring.encode('ascii')).decode('ascii') class CursorPagination(BasePagination): -- cgit v1.2.3 From 83a82b44a56a303d43a16dd675fae116e51b9d85 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 22 Jan 2015 15:07:01 +0000 Subject: Support for tuple ordering in cursor pagination --- rest_framework/pagination.py | 113 +++++++++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 46 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index cf1f1afa..58223f49 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -20,12 +20,12 @@ from rest_framework.utils.urls import ( ) -def _strict_positive_int(integer_string, cutoff=None): +def _positive_int(integer_string, strict=False, cutoff=None): """ Cast a string to a strictly positive integer. """ ret = int(integer_string) - if ret <= 0: + if ret < 0 or (ret == 0 and strict): raise ValueError() if cutoff: ret = min(ret, cutoff) @@ -126,6 +126,47 @@ def _get_page_links(page_numbers, current, url_func): return page_links +def _decode_cursor(encoded): + """ + Given a string representing an encoded cursor, return a `Cursor` instance. + """ + try: + querystring = b64decode(encoded.encode('ascii')).decode('ascii') + tokens = urlparse.parse_qs(querystring, keep_blank_values=True) + offset = _positive_int(tokens['offset'][0]) + reverse = bool(int(tokens['reverse'][0])) + position = tokens.get('position', [None])[0] + except (TypeError, ValueError): + return None + + return Cursor(offset=offset, reverse=reverse, position=position) + + +def _encode_cursor(cursor): + """ + Given a Cursor instance, return an encoded string representation. + """ + tokens = { + 'offset': str(cursor.offset), + 'reverse': '1' if cursor.reverse else '0', + } + if cursor.position is not None: + tokens['position'] = cursor.position + + querystring = urlparse.urlencode(tokens, doseq=True) + return b64encode(querystring.encode('ascii')).decode('ascii') + + +def _reverse_ordering(ordering_tuple): + """ + Given an order_by tuple such as `('-created', 'uuid')` reverse the + ordering and return a new tuple, eg. `('created', '-uuid')`. + """ + invert = lambda x: x[1:] if (x.startswith('-')) else '-' + x + return tuple([invert(item) for item in ordering_tuple]) + + +Cursor = namedtuple('Cursor', ['offset', 'reverse', 'position']) PageLink = namedtuple('PageLink', ['url', 'number', 'is_active', 'is_break']) PAGE_BREAK = PageLink(url=None, number=None, is_active=False, is_break=True) @@ -228,8 +269,9 @@ class PageNumberPagination(BasePagination): def get_page_size(self, request): if self.paginate_by_param: try: - return _strict_positive_int( + return _positive_int( request.query_params[self.paginate_by_param], + strict=True, cutoff=self.max_paginate_by ) except (KeyError, ValueError): @@ -312,7 +354,7 @@ class LimitOffsetPagination(BasePagination): def get_limit(self, request): if self.limit_query_param: try: - return _strict_positive_int( + return _positive_int( request.query_params[self.limit_query_param], cutoff=self.max_limit ) @@ -323,7 +365,7 @@ class LimitOffsetPagination(BasePagination): def get_offset(self, request): try: - return _strict_positive_int( + return _positive_int( request.query_params[self.offset_query_param], ) except (KeyError, ValueError): @@ -384,36 +426,10 @@ class LimitOffsetPagination(BasePagination): return template.render(context) -Cursor = namedtuple('Cursor', ['offset', 'reverse', 'position']) - - -def decode_cursor(encoded): - try: - querystring = b64decode(encoded.encode('ascii')).decode('ascii') - tokens = urlparse.parse_qs(querystring, keep_blank_values=True) - offset = int(tokens['offset'][0]) - reverse = bool(int(tokens['reverse'][0])) - position = tokens['position'][0] - except (TypeError, ValueError): - return None - - return Cursor(offset=offset, reverse=reverse, position=position) - - -def encode_cursor(cursor): - tokens = { - 'offset': str(cursor.offset), - 'reverse': '1' if cursor.reverse else '0', - 'position': cursor.position - } - querystring = urlparse.urlencode(tokens, doseq=True) - return b64encode(querystring.encode('ascii')).decode('ascii') - - class CursorPagination(BasePagination): - # TODO: handle queries with '' as a legitimate position # Support case where ordering is already negative # Support tuple orderings + # Determine how/if True, False and None positions work cursor_query_param = 'cursor' page_size = api_settings.PAGINATE_BY invalid_cursor_message = _('Invalid cursor') @@ -426,25 +442,26 @@ class CursorPagination(BasePagination): encoded = request.query_params.get(self.cursor_query_param) if encoded is None: self.cursor = None - (offset, reverse, current_position) = (0, False, '') + (offset, reverse, current_position) = (0, False, None) else: - self.cursor = decode_cursor(encoded) + self.cursor = _decode_cursor(encoded) if self.cursor is None: raise NotFound(self.invalid_cursor_message) (offset, reverse, current_position) = self.cursor # Cursor pagination always enforces an ordering. if reverse: - queryset = queryset.order_by('-' + self.ordering) + queryset = queryset.order_by(_reverse_ordering(self.ordering)) else: queryset = queryset.order_by(self.ordering) # If we have a cursor with a fixed position then filter by that. - if current_position != '': + if current_position is not None: + primary_ordering_attr = self.ordering[0].lstrip('-') if self.cursor.reverse: - kwargs = {self.ordering + '__lt': current_position} + kwargs = {primary_ordering_attr + '__lt': current_position} else: - kwargs = {self.ordering + '__gt': current_position} + kwargs = {primary_ordering_attr + '__gt': current_position} queryset = queryset.filter(**kwargs) # If we have an offset cursor then offset the entire page by that amount. @@ -468,7 +485,7 @@ class CursorPagination(BasePagination): if reverse: # Determine next and previous positions for reverse cursors. - self.has_next = current_position != '' or offset > 0 + self.has_next = (current_position is not None) or (offset > 0) self.has_previous = has_following_postion if self.has_next: self.next_position = current_position @@ -477,7 +494,7 @@ class CursorPagination(BasePagination): else: # Determine next and previous positions for forward cursors. self.has_next = has_following_postion - self.has_previous = current_position != '' or offset > 0 + self.has_previous = (current_position is not None) or (offset > 0) if self.has_next: self.next_position = following_position if self.has_previous: @@ -518,7 +535,7 @@ class CursorPagination(BasePagination): # Our cursor will have an offset equal to the page size, # but no position to filter against yet. offset = self.page_size - position = '' + position = None elif self.cursor.reverse: # The change in direction will introduce a paging artifact, # where we end up skipping forward a few extra items. @@ -531,7 +548,7 @@ class CursorPagination(BasePagination): position = self.previous_position cursor = Cursor(offset=offset, reverse=False, position=position) - encoded = encode_cursor(cursor) + encoded = _encode_cursor(cursor) return replace_query_param(self.base_url, self.cursor_query_param, encoded) def get_previous_link(self): @@ -567,7 +584,7 @@ class CursorPagination(BasePagination): # Our cursor will have an offset equal to the page size, # but no position to filter against yet. offset = self.page_size - position = '' + position = None elif self.cursor.reverse: # Use the position from the existing cursor and increment # it's offset by the page size. @@ -580,11 +597,15 @@ class CursorPagination(BasePagination): position = self.next_position cursor = Cursor(offset=offset, reverse=True, position=position) - encoded = encode_cursor(cursor) + encoded = _encode_cursor(cursor) return replace_query_param(self.base_url, self.cursor_query_param, encoded) def get_ordering(self): - return 'created' + """ + Return a tuple of strings, that may be used in an `order_by` method. + """ + return ('created',) def _get_position_from_instance(self, instance, ordering): - return str(getattr(instance, ordering)) + attr = getattr(instance, ordering[0]) + return six.text_type(attr) -- cgit v1.2.3 From 408261ee02b176732b7f840f7042e7c24f3ecd27 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 22 Jan 2015 15:15:52 +0000 Subject: Support ordering attribute either on view or on pagination class for CursorPagination --- rest_framework/pagination.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 58223f49..7b28b47f 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -427,16 +427,16 @@ class LimitOffsetPagination(BasePagination): class CursorPagination(BasePagination): - # Support case where ordering is already negative - # Support tuple orderings + # Support usage with OrderingFilter # Determine how/if True, False and None positions work cursor_query_param = 'cursor' page_size = api_settings.PAGINATE_BY invalid_cursor_message = _('Invalid cursor') + ordering = None def paginate_queryset(self, queryset, request, view=None): self.base_url = request.build_absolute_uri() - self.ordering = self.get_ordering() + self.ordering = self.get_ordering(view) # Determine if we have a cursor, and if so then decode it. encoded = request.query_params.get(self.cursor_query_param) @@ -600,11 +600,25 @@ class CursorPagination(BasePagination): encoded = _encode_cursor(cursor) return replace_query_param(self.base_url, self.cursor_query_param, encoded) - def get_ordering(self): + def get_ordering(self, view): """ Return a tuple of strings, that may be used in an `order_by` method. """ - return ('created',) + ordering = getattr(view, 'ordering', getattr(self, 'ordering', None)) + + assert ordering is not None, ( + 'Using cursor pagination, but no ordering attribute was declared ' + 'on the view or on the pagination class.' + ) + assert isinstance(ordering, (six.string_types, list, tuple)), ( + 'Invalid ordering. Expected string or tuple, but got {type}'.format( + type=type(ordering).__name__ + ) + ) + + if isinstance(ordering, six.string_types): + return (ordering,) + return ordering def _get_position_from_instance(self, instance, ordering): attr = getattr(instance, ordering[0]) -- cgit v1.2.3 From 0822c9e55820f8e4737329e38abc2e21718af9e5 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 22 Jan 2015 16:12:05 +0000 Subject: Cursor pagination now works with OrderingFilter --- rest_framework/filters.py | 24 +++++++++++------------- rest_framework/pagination.py | 41 +++++++++++++++++++++++++++++++---------- 2 files changed, 42 insertions(+), 23 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/filters.py b/rest_framework/filters.py index d188a2d1..2bcf3699 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -114,7 +114,7 @@ class OrderingFilter(BaseFilterBackend): ordering_param = api_settings.ORDERING_PARAM ordering_fields = None - def get_ordering(self, request): + def get_ordering(self, request, queryset, view): """ Ordering is set by a comma delimited ?ordering=... query parameter. @@ -124,7 +124,13 @@ class OrderingFilter(BaseFilterBackend): """ params = request.query_params.get(self.ordering_param) if params: - return [param.strip() for param in params.split(',')] + fields = [param.strip() for param in params.split(',')] + ordering = self.remove_invalid_fields(queryset, fields, view) + if ordering: + return ordering + + # No ordering was included, or all the ordering fields were invalid + return self.get_default_ordering(view) def get_default_ordering(self, view): ordering = getattr(view, 'ordering', None) @@ -132,7 +138,7 @@ class OrderingFilter(BaseFilterBackend): return (ordering,) return ordering - def remove_invalid_fields(self, queryset, ordering, view): + def remove_invalid_fields(self, queryset, fields, view): valid_fields = getattr(view, 'ordering_fields', self.ordering_fields) if valid_fields is None: @@ -152,18 +158,10 @@ class OrderingFilter(BaseFilterBackend): valid_fields = [field.name for field in queryset.model._meta.fields] valid_fields += queryset.query.aggregates.keys() - return [term for term in ordering if term.lstrip('-') in valid_fields] + return [term for term in fields if term.lstrip('-') in valid_fields] def filter_queryset(self, request, queryset, view): - ordering = self.get_ordering(request) - - if ordering: - # Skip any incorrect parameters - ordering = self.remove_invalid_fields(queryset, ordering, view) - - if not ordering: - # Use 'ordering' attribute by default - ordering = self.get_default_ordering(view) + ordering = self.get_ordering(request, queryset, view) if ordering: return queryset.order_by(*ordering) diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 7b28b47f..1b4174bc 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -427,8 +427,9 @@ class LimitOffsetPagination(BasePagination): class CursorPagination(BasePagination): - # Support usage with OrderingFilter - # Determine how/if True, False and None positions work + # Determine how/if True, False and None positions work - do the string + # encodings work with Django queryset filters? + # Consider a max offset cap. cursor_query_param = 'cursor' page_size = api_settings.PAGINATE_BY invalid_cursor_message = _('Invalid cursor') @@ -436,7 +437,7 @@ class CursorPagination(BasePagination): def paginate_queryset(self, queryset, request, view=None): self.base_url = request.build_absolute_uri() - self.ordering = self.get_ordering(view) + self.ordering = self.get_ordering(request, queryset, view) # Determine if we have a cursor, and if so then decode it. encoded = request.query_params.get(self.cursor_query_param) @@ -600,16 +601,36 @@ class CursorPagination(BasePagination): encoded = _encode_cursor(cursor) return replace_query_param(self.base_url, self.cursor_query_param, encoded) - def get_ordering(self, view): + def get_ordering(self, request, queryset, view): """ Return a tuple of strings, that may be used in an `order_by` method. """ - ordering = getattr(view, 'ordering', getattr(self, 'ordering', None)) + ordering_filters = [ + filter_cls for filter_cls in getattr(view, 'filter_backends', []) + if hasattr(filter_cls, 'get_ordering') + ] + + if ordering_filters: + # If a filter exists on the view that implements `get_ordering` + # then we defer to that filter to determine the ordering. + filter_cls = ordering_filters[0] + filter_instance = filter_cls() + ordering = filter_instance.get_ordering(request, queryset, view) + assert ordering is not None, ( + 'Using cursor pagination, but filter class {filter_cls} ' + 'returned a `None` ordering.'.format( + filter_cls=filter_cls.__name__ + ) + ) + else: + # The default case is to check for an `ordering` attribute, + # first on the view instance, and then on this pagination instance. + ordering = getattr(view, 'ordering', getattr(self, 'ordering', None)) + assert ordering is not None, ( + 'Using cursor pagination, but no ordering attribute was declared ' + 'on the view or on the pagination class.' + ) - assert ordering is not None, ( - 'Using cursor pagination, but no ordering attribute was declared ' - 'on the view or on the pagination class.' - ) assert isinstance(ordering, (six.string_types, list, tuple)), ( 'Invalid ordering. Expected string or tuple, but got {type}'.format( type=type(ordering).__name__ @@ -618,7 +639,7 @@ class CursorPagination(BasePagination): if isinstance(ordering, six.string_types): return (ordering,) - return ordering + return tuple(ordering) def _get_position_from_instance(self, instance, ordering): attr = getattr(instance, ordering[0]) -- cgit v1.2.3 From 43d983fae82ab23ca94f52deb29e938eb2a40e88 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 22 Jan 2015 17:25:12 +0000 Subject: Add paging controls --- rest_framework/pagination.py | 66 +++++++++++++++++----- .../static/rest_framework/css/bootstrap-tweaks.css | 12 +++- .../pagination/previous_and_next.html | 12 ++++ 3 files changed, 74 insertions(+), 16 deletions(-) create mode 100644 rest_framework/templates/rest_framework/pagination/previous_and_next.html (limited to 'rest_framework') diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 1b4174bc..b3658aca 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -133,9 +133,14 @@ def _decode_cursor(encoded): try: querystring = b64decode(encoded.encode('ascii')).decode('ascii') tokens = urlparse.parse_qs(querystring, keep_blank_values=True) - offset = _positive_int(tokens['offset'][0]) - reverse = bool(int(tokens['reverse'][0])) - position = tokens.get('position', [None])[0] + + offset = tokens.get('o', ['0'])[0] + offset = _positive_int(offset) + + reverse = tokens.get('r', ['0'])[0] + reverse = bool(int(reverse)) + + position = tokens.get('p', [None])[0] except (TypeError, ValueError): return None @@ -146,12 +151,13 @@ def _encode_cursor(cursor): """ Given a Cursor instance, return an encoded string representation. """ - tokens = { - 'offset': str(cursor.offset), - 'reverse': '1' if cursor.reverse else '0', - } + tokens = {} + if cursor.offset != 0: + tokens['o'] = str(cursor.offset) + if cursor.reverse: + tokens['r'] = '1' if cursor.position is not None: - tokens['position'] = cursor.position + tokens['p'] = cursor.position querystring = urlparse.urlencode(tokens, doseq=True) return b64encode(querystring.encode('ascii')).decode('ascii') @@ -430,10 +436,12 @@ class CursorPagination(BasePagination): # Determine how/if True, False and None positions work - do the string # encodings work with Django queryset filters? # Consider a max offset cap. + # Tidy up the `get_ordering` API (eg remove queryset from it) cursor_query_param = 'cursor' page_size = api_settings.PAGINATE_BY invalid_cursor_message = _('Invalid cursor') ordering = None + template = 'rest_framework/pagination/previous_and_next.html' def paginate_queryset(self, queryset, request, view=None): self.base_url = request.build_absolute_uri() @@ -452,17 +460,22 @@ class CursorPagination(BasePagination): # Cursor pagination always enforces an ordering. if reverse: - queryset = queryset.order_by(_reverse_ordering(self.ordering)) + queryset = queryset.order_by(*_reverse_ordering(self.ordering)) else: - queryset = queryset.order_by(self.ordering) + queryset = queryset.order_by(*self.ordering) # If we have a cursor with a fixed position then filter by that. if current_position is not None: - primary_ordering_attr = self.ordering[0].lstrip('-') - if self.cursor.reverse: - kwargs = {primary_ordering_attr + '__lt': current_position} + order = self.ordering[0] + is_reversed = order.startswith('-') + order_attr = order.lstrip('-') + + # Test for: (cursor reversed) XOR (queryset reversed) + if self.cursor.reverse != is_reversed: + kwargs = {order_attr + '__lt': current_position} else: - kwargs = {primary_ordering_attr + '__gt': current_position} + kwargs = {order_attr + '__gt': current_position} + queryset = queryset.filter(**kwargs) # If we have an offset cursor then offset the entire page by that amount. @@ -501,6 +514,11 @@ class CursorPagination(BasePagination): if self.has_previous: self.previous_position = current_position + # Display page controls in the browsable API if there is more + # than one page. + if self.has_previous or self.has_next: + self.display_page_controls = True + return self.page def get_next_link(self): @@ -642,5 +660,23 @@ class CursorPagination(BasePagination): return tuple(ordering) def _get_position_from_instance(self, instance, ordering): - attr = getattr(instance, ordering[0]) + attr = getattr(instance, ordering[0].lstrip('-')) return six.text_type(attr) + + def get_paginated_response(self, data): + return Response(OrderedDict([ + ('next', self.get_next_link()), + ('previous', self.get_previous_link()), + ('results', data) + ])) + + def get_html_context(self): + return { + 'previous_url': self.get_previous_link(), + 'next_url': self.get_next_link() + } + + def to_html(self): + template = loader.get_template(self.template) + context = Context(self.get_html_context()) + return template.render(context) diff --git a/rest_framework/static/rest_framework/css/bootstrap-tweaks.css b/rest_framework/static/rest_framework/css/bootstrap-tweaks.css index 15b42178..04f12ed3 100644 --- a/rest_framework/static/rest_framework/css/bootstrap-tweaks.css +++ b/rest_framework/static/rest_framework/css/bootstrap-tweaks.css @@ -63,10 +63,20 @@ a single block in the template. .pagination>.disabled>a, .pagination>.disabled>a:hover, .pagination>.disabled>a:focus { - cursor: default; + cursor: not-allowed; pointer-events: none; } +.pager>.disabled>a, +.pager>.disabled>a:hover, +.pager>.disabled>a:focus { + pointer-events: none; +} + +.pager .next { + margin-left: 10px; +} + /*=== dabapps bootstrap styles ====*/ html { diff --git a/rest_framework/templates/rest_framework/pagination/previous_and_next.html b/rest_framework/templates/rest_framework/pagination/previous_and_next.html new file mode 100644 index 00000000..eacbfff4 --- /dev/null +++ b/rest_framework/templates/rest_framework/pagination/previous_and_next.html @@ -0,0 +1,12 @@ + -- cgit v1.2.3 From 25a703b42c030f712734ed56b8f1996f8d13ac0c Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 23 Jan 2015 11:15:11 +0000 Subject: Work around meta API differences --- rest_framework/utils/model_meta.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/utils/model_meta.py b/rest_framework/utils/model_meta.py index 375d2e8c..6a5835f5 100644 --- a/rest_framework/utils/model_meta.py +++ b/rest_framework/utils/model_meta.py @@ -121,12 +121,17 @@ def _get_reverse_relationships(opts): """ Returns an `OrderedDict` of field names to `RelationInfo`. """ + # Note that we have a hack here to handle internal API differences for + # this internal API across Django 1.7 -> Django 1.8. + # See: https://code.djangoproject.com/ticket/24208 + reverse_relations = OrderedDict() for relation in opts.get_all_related_objects(): accessor_name = relation.get_accessor_name() + related = getattr(relation, 'related_model', relation.model) reverse_relations[accessor_name] = RelationInfo( model_field=None, - related=relation.model, + related=related, to_many=relation.field.rel.multiple, has_through_model=False ) @@ -134,9 +139,10 @@ def _get_reverse_relationships(opts): # Deal with reverse many-to-many relationships. for relation in opts.get_all_related_many_to_many_objects(): accessor_name = relation.get_accessor_name() + related = getattr(relation, 'related_model', relation.model) reverse_relations[accessor_name] = RelationInfo( model_field=None, - related=relation.model, + related=related, to_many=True, has_through_model=( (getattr(relation.field.rel, 'through', None) is not None) -- cgit v1.2.3 From f1ac9d3f9b6c306b7fa48381006d8259c1642a99 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 23 Jan 2015 12:26:44 +0000 Subject: More graceful handling of malformed Content-Disposition --- rest_framework/parsers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py index 401856ec..ef72677c 100644 --- a/rest_framework/parsers.py +++ b/rest_framework/parsers.py @@ -298,7 +298,7 @@ class FileUploadParser(BaseParser): if 'filename*' in filename_parm: return self.get_encoded_filename(filename_parm) return force_text(filename_parm['filename']) - except (AttributeError, KeyError): + except (AttributeError, KeyError, ValueError): pass def get_encoded_filename(self, filename_parm): -- cgit v1.2.3 From f3b6eedb8aeaa23f4b48551356814837973db31c Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 23 Jan 2015 12:56:55 +0000 Subject: More sensible response caching. --- rest_framework/response.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/response.py b/rest_framework/response.py index d6ca1aad..7f90bae1 100644 --- a/rest_framework/response.py +++ b/rest_framework/response.py @@ -81,10 +81,13 @@ class Response(SimpleTemplateResponse): def __getstate__(self): """ - Remove attributes from the response that shouldn't be cached + Remove attributes from the response that shouldn't be cached. """ state = super(Response, self).__getstate__() - for key in ('accepted_renderer', 'renderer_context', 'data'): + for key in ( + 'accepted_renderer', 'renderer_context', 'resolver_match', + 'client', 'request', 'wsgi_request', '_closable_objects' + ): if key in state: del state[key] return state -- cgit v1.2.3 From 8f25c0c53c24c88afc86d99bbb3ca4edc3a4e0a2 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 23 Jan 2015 14:56:15 +0000 Subject: Add 1.8 support --- rest_framework/serializers.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index b91ecebc..d9a67441 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -12,7 +12,7 @@ response content is handled by parsers and renderers. """ from __future__ import unicode_literals from django.db import models -from django.db.models.fields import FieldDoesNotExist +from django.db.models.fields import FieldDoesNotExist, Field as DjangoModelField from django.utils.translation import ugettext_lazy as _ from rest_framework.compat import unicode_to_repr from rest_framework.utils import model_meta @@ -1231,7 +1231,9 @@ class ModelSerializer(Serializer): continue try: - model_fields[source] = model._meta.get_field(source) + field = model._meta.get_field(source) + if isinstance(field, DjangoModelField): + model_fields[source] = field except FieldDoesNotExist: pass -- cgit v1.2.3 From e8db1834d3a3f6ba05276b64e5681288aa8f9820 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 23 Jan 2015 15:24:06 +0000 Subject: Added UUIDField. --- rest_framework/fields.py | 18 ++++++++++++++++++ rest_framework/serializers.py | 8 +++++++- 2 files changed, 25 insertions(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/fields.py b/rest_framework/fields.py index cc9410aa..5e3f7ce4 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -23,6 +23,7 @@ import datetime import decimal import inspect import re +import uuid class empty: @@ -632,6 +633,23 @@ class URLField(CharField): self.validators.append(validator) +class UUIDField(Field): + default_error_messages = { + 'invalid': _('"{value}" is not a valid UUID.'), + } + + def to_internal_value(self, data): + if not isinstance(data, uuid.UUID): + try: + return uuid.UUID(data) + except (ValueError, TypeError): + self.fail('invalid', value=data) + return data + + def to_representation(self, value): + return str(value) + + # Number types... class IntegerField(Field): diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index cf797bdc..dca612ca 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -702,6 +702,7 @@ class ModelSerializer(Serializer): you need you should either declare the extra/differing fields explicitly on the serializer class, or simply use a `Serializer` class. """ + _field_mapping = ClassLookupDict({ models.AutoField: IntegerField, models.BigIntegerField: IntegerField, @@ -724,7 +725,8 @@ class ModelSerializer(Serializer): models.SmallIntegerField: IntegerField, models.TextField: CharField, models.TimeField: TimeField, - models.URLField: URLField, + models.URLField: URLField + # Note: Some version-specific mappings also defined below. }) _related_class = PrimaryKeyRelatedField @@ -1132,6 +1134,10 @@ class ModelSerializer(Serializer): return NestedSerializer +if hasattr(models, 'UUIDField'): + ModelSerializer._field_mapping[models.UUIDField] = UUIDField + + class HyperlinkedModelSerializer(ModelSerializer): """ A type of `ModelSerializer` that uses hyperlinked relationships instead -- cgit v1.2.3 From 889a07f5563a0f970639a0958c0dcbc26e82919f Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 23 Jan 2015 15:32:21 +0000 Subject: Support assignment in ClassLookupDict --- rest_framework/utils/field_mapping.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'rest_framework') diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index cba40d31..c97ec5d0 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -38,6 +38,9 @@ class ClassLookupDict(object): return self.mapping[cls] raise KeyError('Class %s not found in lookup.', cls.__name__) + def __setitem__(self, key, value): + self.mapping[key] = value + def needs_label(model_field, field_name): """ -- cgit v1.2.3 From 35f6a8246299d31ecce4f791f9527bf34cebe6e2 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 23 Jan 2015 16:27:23 +0000 Subject: Added DictField and support for HStoreField. --- rest_framework/compat.py | 7 +++++ rest_framework/fields.py | 59 +++++++++++++++++++++++++++++++++++++++++-- rest_framework/serializers.py | 8 +++++- 3 files changed, 71 insertions(+), 3 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 766afaec..36413394 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -58,6 +58,13 @@ except ImportError: from django.http import HttpResponse as HttpResponseBase +# contrib.postgres only supported from 1.8 onwards. +try: + from django.contrib.postgres import fields as postgres_fields +except ImportError: + postgres_fields = None + + # request only provides `resolver_match` from 1.5 onwards. def get_resolver_match(request): try: diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 5e3f7ce4..71a9f193 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1132,8 +1132,21 @@ class ImageField(FileField): # Composite field types... +class _UnvalidatedField(Field): + def __init__(self, *args, **kwargs): + super(_UnvalidatedField, self).__init__(*args, **kwargs) + self.allow_blank = True + self.allow_null = True + + def to_internal_value(self, data): + return data + + def to_representation(self, value): + return value + + class ListField(Field): - child = None + child = _UnvalidatedField() initial = [] default_error_messages = { 'not_a_list': _('Expected a list of items but got type `{input_type}`') @@ -1141,7 +1154,6 @@ class ListField(Field): def __init__(self, *args, **kwargs): self.child = kwargs.pop('child', copy.deepcopy(self.child)) - assert self.child is not None, '`child` is a required argument.' assert not inspect.isclass(self.child), '`child` has not been instantiated.' super(ListField, self).__init__(*args, **kwargs) self.child.bind(field_name='', parent=self) @@ -1170,6 +1182,49 @@ class ListField(Field): return [self.child.to_representation(item) for item in data] +class DictField(Field): + child = _UnvalidatedField() + initial = [] + default_error_messages = { + 'not_a_dict': _('Expected a dictionary of items but got type `{input_type}`') + } + + def __init__(self, *args, **kwargs): + self.child = kwargs.pop('child', copy.deepcopy(self.child)) + assert not inspect.isclass(self.child), '`child` has not been instantiated.' + super(DictField, self).__init__(*args, **kwargs) + self.child.bind(field_name='', parent=self) + + def get_value(self, dictionary): + # We override the default field access in order to support + # lists in HTML forms. + if html.is_html_input(dictionary): + return html.parse_html_list(dictionary, prefix=self.field_name) + return dictionary.get(self.field_name, empty) + + def to_internal_value(self, data): + """ + Dicts of native values <- Dicts of primitive datatypes. + """ + if html.is_html_input(data): + data = html.parse_html_dict(data) + if not isinstance(data, dict): + self.fail('not_a_dict', input_type=type(data).__name__) + return dict([ + (six.text_type(key), self.child.run_validation(value)) + for key, value in data.items() + ]) + + def to_representation(self, value): + """ + List of object instances -> List of dicts of primitive datatypes. + """ + return dict([ + (six.text_type(key), self.child.to_representation(val)) + for key, val in value.items() + ]) + + # Miscellaneous field types... class ReadOnlyField(Field): diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index dca612ca..42d1e370 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -14,7 +14,7 @@ from __future__ import unicode_literals from django.db import models from django.db.models.fields import FieldDoesNotExist, Field as DjangoField from django.utils.translation import ugettext_lazy as _ -from rest_framework.compat import unicode_to_repr +from rest_framework.compat import postgres_fields, unicode_to_repr from rest_framework.utils import model_meta from rest_framework.utils.field_mapping import ( get_url_kwargs, get_field_kwargs, @@ -1137,6 +1137,12 @@ class ModelSerializer(Serializer): if hasattr(models, 'UUIDField'): ModelSerializer._field_mapping[models.UUIDField] = UUIDField +if postgres_fields: + class CharMappingField(DictField): + child = CharField() + + ModelSerializer._field_mapping[postgres_fields.HStoreField] = CharMappingField + class HyperlinkedModelSerializer(ModelSerializer): """ -- cgit v1.2.3 From a1fa7218ebc4a77a3912c42221927b1846f555fd Mon Sep 17 00:00:00 2001 From: Alexander Dutton Date: Fri, 23 Jan 2015 16:48:23 +0000 Subject: Pass {} as data to DataAndFiles, as it ends up in a MergeDict In the same vein as #2399. --- rest_framework/parsers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py index ef72677c..1efab85b 100644 --- a/rest_framework/parsers.py +++ b/rest_framework/parsers.py @@ -250,7 +250,7 @@ class FileUploadParser(BaseParser): None, encoding) if result is not None: - return DataAndFiles(None, {'file': result[1]}) + return DataAndFiles({}, {'file': result[1]}) # This is the standard case. possible_sizes = [x.chunk_size for x in upload_handlers if x.chunk_size] -- cgit v1.2.3 From ed04725822d5dc9a90c9c6e5c14d85083ae6ff28 Mon Sep 17 00:00:00 2001 From: Brandon Cazander Date: Sat, 24 Jan 2015 01:44:40 -0800 Subject: Use enhanced request when cloning requests for checking permissions on other methods. Fixes #2455 --- rest_framework/request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/request.py b/rest_framework/request.py index cfbbdecc..d5b56ada 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -86,7 +86,7 @@ def clone_request(request, method): Internal helper method to clone a request, replacing with a different HTTP method. Used for checking permissions against other methods. """ - ret = Request(request=request._request, + ret = Request(request=request, parsers=request.parsers, authenticators=request.authenticators, negotiator=request.negotiator, -- cgit v1.2.3 From bf58c1265ddf06deb435d049fae01ed324a310e0 Mon Sep 17 00:00:00 2001 From: Brandon Cazander Date: Mon, 26 Jan 2015 22:56:57 -0800 Subject: Set a version attribute on cloned requests if necessary. --- rest_framework/request.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/request.py b/rest_framework/request.py index d5b56ada..ce2fcb47 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -86,7 +86,7 @@ def clone_request(request, method): Internal helper method to clone a request, replacing with a different HTTP method. Used for checking permissions against other methods. """ - ret = Request(request=request, + ret = Request(request=request._request, parsers=request.parsers, authenticators=request.authenticators, negotiator=request.negotiator, @@ -107,6 +107,8 @@ def clone_request(request, method): ret.accepted_renderer = request.accepted_renderer if hasattr(request, 'accepted_media_type'): ret.accepted_media_type = request.accepted_media_type + if hasattr(request, 'version'): + ret.version = request.version return ret -- cgit v1.2.3 From 65bca59ea548dc5e2222be06ca20b3d3fa151cf0 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 27 Jan 2015 13:51:30 +0000 Subject: Reload api_settings when using Django's 'override_settings' --- rest_framework/settings.py | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'rest_framework') diff --git a/rest_framework/settings.py b/rest_framework/settings.py index fc6dfecd..e5e5edaf 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -18,6 +18,7 @@ REST framework settings, checking for user settings first, then falling back to the defaults. """ from __future__ import unicode_literals +from django.test.signals import setting_changed from django.conf import settings from django.utils import importlib, six from rest_framework import ISO_8601 @@ -198,3 +199,13 @@ class APISettings(object): api_settings = APISettings(USER_SETTINGS, DEFAULTS, IMPORT_STRINGS) + + +def reload_api_settings(*args, **kwargs): + global api_settings + setting, value = kwargs['setting'], kwargs['value'] + if setting == 'REST_FRAMEWORK': + api_settings = APISettings(value, DEFAULTS, IMPORT_STRINGS) + + +setting_changed.connect(reload_api_settings) -- cgit v1.2.3 From 5b369bf5fe3e5e8af3a73055b3a6ebda1e88f68e Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Tue, 27 Jan 2015 19:45:37 +0100 Subject: Bumped the version. --- rest_framework/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index fdcebb7b..57e5421b 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ ______ _____ _____ _____ __ """ __title__ = 'Django REST framework' -__version__ = '3.0.3' +__version__ = '3.0.4' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2015 Tom Christie' -- cgit v1.2.3 From 8c3f82fb18a58b8e0983612ef3cc35b3c3950b66 Mon Sep 17 00:00:00 2001 From: Susan Dreher Date: Tue, 27 Jan 2015 16:18:51 -0500 Subject: :bug: ManyRelatedField get_value clearing field on partial update A PATCH to a serializer's non-related CharField was clearing an ancillary StringRelatedField(many=True) field. The issue appears to be in the ManyRelatedField's get_value method, which was returning a [] instead of empty when the request data was a MultiDict. This fix mirrors code in fields.py, class Field, get_value, Ln. 272, which explicitly returns empty on a partial update. Tests added to demonstrate the issue. --- rest_framework/relations.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'rest_framework') diff --git a/rest_framework/relations.py b/rest_framework/relations.py index aa0c2def..13793f37 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -338,7 +338,12 @@ class ManyRelatedField(Field): # We override the default field access in order to support # lists in HTML forms. if html.is_html_input(dictionary): + # Don't return [] if the update is partial + if self.field_name not in dictionary: + if getattr(self.root, 'partial', False): + return empty return dictionary.getlist(self.field_name) + return dictionary.get(self.field_name, empty) def to_internal_value(self, data): -- cgit v1.2.3 From ac87490b91e3405d497da360afed10842a73dfd0 Mon Sep 17 00:00:00 2001 From: Brandon Cazander Date: Tue, 27 Jan 2015 17:10:17 -0800 Subject: Clone the versioning_scheme when necessary. Fixes #2477 --- rest_framework/request.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'rest_framework') diff --git a/rest_framework/request.py b/rest_framework/request.py index ce2fcb47..bf6ff670 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -109,6 +109,8 @@ def clone_request(request, method): ret.accepted_media_type = request.accepted_media_type if hasattr(request, 'version'): ret.version = request.version + if hasattr(request, 'versioning_scheme'): + ret.versioning_scheme = request.versioning_scheme return ret -- cgit v1.2.3 From a1eba885e287f59dd269441dfebb3b3de3eea692 Mon Sep 17 00:00:00 2001 From: Lucas Wiman Date: Tue, 27 Jan 2015 19:01:40 -0800 Subject: Use the proper db_table argument when constructing meta --- rest_framework/authtoken/south_migrations/0001_initial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/authtoken/south_migrations/0001_initial.py b/rest_framework/authtoken/south_migrations/0001_initial.py index 926de02b..5b927f3e 100644 --- a/rest_framework/authtoken/south_migrations/0001_initial.py +++ b/rest_framework/authtoken/south_migrations/0001_initial.py @@ -40,7 +40,7 @@ class Migration(SchemaMigration): 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) }, "%s.%s" % (User._meta.app_label, User._meta.module_name): { - 'Meta': {'object_name': User._meta.module_name}, + 'Meta': {'object_name': User._meta.module_name, 'db_table': repr(User._meta.db_table)}, }, 'authtoken.token': { 'Meta': {'object_name': 'Token'}, -- cgit v1.2.3 From ba7dca893cd55a1d5ee928c4b10878c92c44c4f5 Mon Sep 17 00:00:00 2001 From: Tymur Maryokhin Date: Thu, 29 Jan 2015 17:28:03 +0100 Subject: Removed router check for deprecated '.model' attribute --- rest_framework/routers.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 827da034..6a4184e2 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -130,19 +130,13 @@ class SimpleRouter(BaseRouter): If `base_name` is not specified, attempt to automatically determine it from the viewset. """ - # Note that `.model` attribute on views is deprecated, although we - # enforce the deprecation on the view `get_serializer_class()` and - # `get_queryset()` methods, rather than here. - model_cls = getattr(viewset, 'model', None) queryset = getattr(viewset, 'queryset', None) - if model_cls is None and queryset is not None: - model_cls = queryset.model - assert model_cls, '`base_name` argument not specified, and could ' \ + assert queryset is not None, '`base_name` argument not specified, and could ' \ 'not automatically determine the name from the viewset, as ' \ 'it does not have a `.queryset` attribute.' - return model_cls._meta.object_name.lower() + return queryset.model._meta.object_name.lower() def get_routes(self, viewset): """ -- cgit v1.2.3 From 760b25bc20a1434cbdd69dc0b13bacdc3bbedd7c Mon Sep 17 00:00:00 2001 From: José Padilla Date: Fri, 30 Jan 2015 11:36:03 -0400 Subject: Fix AttributeError on renamed _field_mapping --- rest_framework/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index a3b8196b..a91fe23e 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -1330,13 +1330,13 @@ class ModelSerializer(Serializer): if hasattr(models, 'UUIDField'): - ModelSerializer._field_mapping[models.UUIDField] = UUIDField + ModelSerializer.serializer_field_mapping[models.UUIDField] = UUIDField if postgres_fields: class CharMappingField(DictField): child = CharField() - ModelSerializer._field_mapping[postgres_fields.HStoreField] = CharMappingField + ModelSerializer.serializer_field_mapping[postgres_fields.HStoreField] = CharMappingField class HyperlinkedModelSerializer(ModelSerializer): -- cgit v1.2.3 From 0d96cf2ca2e3298ed38e81482bcdc2664d060735 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 30 Jan 2015 16:27:49 +0000 Subject: Latest translation source messages. --- rest_framework/locale/en_US/LC_MESSAGES/django.po | 112 ++++++++++++---------- 1 file changed, 59 insertions(+), 53 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/locale/en_US/LC_MESSAGES/django.po b/rest_framework/locale/en_US/LC_MESSAGES/django.po index d98225ce..23f76ff7 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: 2015-01-07 18:21+0000\n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -49,18 +49,6 @@ msgstr "" msgid "User inactive or deleted." 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:38 msgid "A server error occurred." msgstr "" @@ -101,28 +89,28 @@ msgstr "" msgid "Request was throttled." msgstr "" -#: fields.py:152 relations.py:131 relations.py:155 validators.py:77 +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 #: validators.py:155 msgid "This field is required." msgstr "" -#: fields.py:153 +#: fields.py:154 msgid "This field may not be null." msgstr "" -#: fields.py:480 fields.py:508 +#: fields.py:487 fields.py:515 msgid "\"{input}\" is not a valid boolean." msgstr "" -#: fields.py:543 +#: fields.py:550 msgid "This field may not be blank." msgstr "" -#: fields.py:544 fields.py:1252 +#: fields.py:551 fields.py:1324 msgid "Ensure this field has no more than {max_length} characters." msgstr "" -#: fields.py:545 +#: fields.py:552 msgid "Ensure this field has at least {min_length} characters." msgstr "" @@ -144,134 +132,140 @@ msgstr "" msgid "Enter a valid URL." msgstr "" -#: fields.py:640 +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 msgid "A valid integer is required." msgstr "" -#: fields.py:641 fields.py:675 fields.py:708 +#: fields.py:658 fields.py:692 fields.py:725 msgid "Ensure this value is less than or equal to {max_value}." msgstr "" -#: fields.py:642 fields.py:676 fields.py:709 +#: fields.py:659 fields.py:693 fields.py:726 msgid "Ensure this value is greater than or equal to {min_value}." msgstr "" -#: fields.py:643 fields.py:677 fields.py:713 +#: fields.py:660 fields.py:694 fields.py:730 msgid "String value too large." msgstr "" -#: fields.py:674 fields.py:707 +#: fields.py:691 fields.py:724 msgid "A valid number is required." msgstr "" -#: fields.py:710 +#: fields.py:727 msgid "Ensure that there are no more than {max_digits} digits in total." msgstr "" -#: fields.py:711 +#: fields.py:728 msgid "Ensure that there are no more than {max_decimal_places} decimal places." msgstr "" -#: fields.py:712 +#: fields.py:729 msgid "" "Ensure that there are no more than {max_whole_digits} digits before the " "decimal point." msgstr "" -#: fields.py:796 +#: fields.py:813 msgid "Datetime has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:797 +#: fields.py:814 msgid "Expected a datetime but got a date." msgstr "" -#: fields.py:861 +#: fields.py:878 msgid "Date has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:862 +#: fields.py:879 msgid "Expected a date but got a datetime." msgstr "" -#: fields.py:919 +#: fields.py:936 msgid "Time has wrong format. Use one of these formats instead: {format}." msgstr "" -#: fields.py:975 fields.py:1019 +#: fields.py:992 fields.py:1036 msgid "\"{input}\" is not a valid choice." msgstr "" -#: fields.py:1020 fields.py:1121 serializers.py:476 +#: fields.py:1037 fields.py:1151 serializers.py:482 msgid "Expected a list of items but got type \"{input_type}\"." msgstr "" -#: fields.py:1050 +#: fields.py:1067 msgid "No file was submitted." msgstr "" -#: fields.py:1051 +#: fields.py:1068 msgid "The submitted data was not a file. Check the encoding type on the form." msgstr "" -#: fields.py:1052 +#: fields.py:1069 msgid "No filename could be determined." msgstr "" -#: fields.py:1053 +#: fields.py:1070 msgid "The submitted file is empty." msgstr "" -#: fields.py:1054 +#: fields.py:1071 msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." msgstr "" -#: fields.py:1096 +#: fields.py:1113 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -#: generics.py:123 -msgid "" -"Choose a valid page number. Page numbers must be a whole number, or must be " -"the string \"last\"." +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." msgstr "" -#: generics.py:128 +#: pagination.py:221 msgid "Invalid page \"{page_number}\": {message}." msgstr "" -#: relations.py:132 -msgid "Invalid pk \"{pk_value}\" - object does not exist." +#: pagination.py:442 +msgid "Invalid cursor" msgstr "" #: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "" + +#: relations.py:134 msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "" -#: relations.py:156 +#: relations.py:157 msgid "Invalid hyperlink - No URL match." msgstr "" -#: relations.py:157 +#: relations.py:158 msgid "Invalid hyperlink - Incorrect URL match." msgstr "" -#: relations.py:158 +#: relations.py:159 msgid "Invalid hyperlink - Object does not exist." msgstr "" -#: relations.py:159 +#: relations.py:160 msgid "Incorrect type. Expected URL string, received {data_type}." msgstr "" -#: relations.py:294 +#: relations.py:295 msgid "Object with {slug_name}={value} does not exist." msgstr "" -#: relations.py:295 +#: relations.py:296 msgid "Invalid value." msgstr "" @@ -314,3 +308,15 @@ msgstr "" #: versioning.py:160 msgid "Invalid version in query parameter." msgstr "" + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "" + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "" + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "" -- cgit v1.2.3 From 6838f17325c2149e432e4a40b945695b765f35a2 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 30 Jan 2015 16:40:54 +0000 Subject: Add built-in translations. --- rest_framework/locale/ar/LC_MESSAGES/django.mo | Bin 0 -> 4875 bytes rest_framework/locale/ar/LC_MESSAGES/django.po | 325 +++++++++++++++++++++ rest_framework/locale/cs/LC_MESSAGES/django.mo | Bin 0 -> 8848 bytes rest_framework/locale/cs/LC_MESSAGES/django.po | 325 +++++++++++++++++++++ rest_framework/locale/da/LC_MESSAGES/django.mo | Bin 0 -> 8452 bytes rest_framework/locale/da/LC_MESSAGES/django.po | 325 +++++++++++++++++++++ rest_framework/locale/de/LC_MESSAGES/django.mo | Bin 0 -> 6575 bytes rest_framework/locale/de/LC_MESSAGES/django.po | 325 +++++++++++++++++++++ rest_framework/locale/en/LC_MESSAGES/django.mo | Bin 0 -> 8553 bytes rest_framework/locale/en/LC_MESSAGES/django.po | 324 +++++++++++++++++++++ rest_framework/locale/en_US/LC_MESSAGES/django.mo | Bin 0 -> 378 bytes rest_framework/locale/en_US/LC_MESSAGES/django.po | 8 +- rest_framework/locale/es/LC_MESSAGES/django.mo | Bin 0 -> 8906 bytes rest_framework/locale/es/LC_MESSAGES/django.po | 327 +++++++++++++++++++++ rest_framework/locale/et/LC_MESSAGES/django.mo | Bin 0 -> 2018 bytes rest_framework/locale/et/LC_MESSAGES/django.po | 325 +++++++++++++++++++++ rest_framework/locale/fr/LC_MESSAGES/django.mo | Bin 0 -> 6975 bytes rest_framework/locale/fr/LC_MESSAGES/django.po | 326 +++++++++++++++++++++ rest_framework/locale/hu/LC_MESSAGES/django.mo | Bin 0 -> 9215 bytes rest_framework/locale/hu/LC_MESSAGES/django.po | 325 +++++++++++++++++++++ rest_framework/locale/id/LC_MESSAGES/django.mo | Bin 0 -> 497 bytes rest_framework/locale/id/LC_MESSAGES/django.po | 324 +++++++++++++++++++++ rest_framework/locale/it/LC_MESSAGES/django.mo | Bin 0 -> 5334 bytes rest_framework/locale/it/LC_MESSAGES/django.po | 325 +++++++++++++++++++++ rest_framework/locale/ko_KR/LC_MESSAGES/django.mo | Bin 0 -> 8555 bytes rest_framework/locale/ko_KR/LC_MESSAGES/django.po | 325 +++++++++++++++++++++ rest_framework/locale/mk/LC_MESSAGES/django.mo | Bin 0 -> 10731 bytes rest_framework/locale/mk/LC_MESSAGES/django.po | 325 +++++++++++++++++++++ rest_framework/locale/nl/LC_MESSAGES/django.mo | Bin 0 -> 499 bytes rest_framework/locale/nl/LC_MESSAGES/django.po | 324 +++++++++++++++++++++ rest_framework/locale/pl/LC_MESSAGES/django.mo | Bin 0 -> 8981 bytes rest_framework/locale/pl/LC_MESSAGES/django.po | 326 +++++++++++++++++++++ rest_framework/locale/pt_BR/LC_MESSAGES/django.mo | Bin 0 -> 8729 bytes rest_framework/locale/pt_BR/LC_MESSAGES/django.po | 326 +++++++++++++++++++++ rest_framework/locale/ru/LC_MESSAGES/django.mo | Bin 0 -> 9778 bytes rest_framework/locale/ru/LC_MESSAGES/django.po | 325 +++++++++++++++++++++ rest_framework/locale/sk/LC_MESSAGES/django.mo | Bin 0 -> 2748 bytes rest_framework/locale/sk/LC_MESSAGES/django.po | 325 +++++++++++++++++++++ rest_framework/locale/sv/LC_MESSAGES/django.mo | Bin 0 -> 8647 bytes rest_framework/locale/sv/LC_MESSAGES/django.po | 325 +++++++++++++++++++++ rest_framework/locale/tr/LC_MESSAGES/django.mo | Bin 0 -> 7946 bytes rest_framework/locale/tr/LC_MESSAGES/django.po | 328 ++++++++++++++++++++++ rest_framework/locale/uk/LC_MESSAGES/django.mo | Bin 0 -> 577 bytes rest_framework/locale/uk/LC_MESSAGES/django.po | 324 +++++++++++++++++++++ rest_framework/locale/zh_CN/LC_MESSAGES/django.mo | Bin 0 -> 8383 bytes rest_framework/locale/zh_CN/LC_MESSAGES/django.po | 325 +++++++++++++++++++++ rest_framework/views.py | 10 +- 47 files changed, 7165 insertions(+), 7 deletions(-) create mode 100644 rest_framework/locale/ar/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/ar/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/cs/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/cs/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/da/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/da/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/de/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/de/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/en/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/en/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/en_US/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/es/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/es/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/et/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/et/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/fr/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/fr/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/hu/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/hu/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/id/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/id/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/it/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/it/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/ko_KR/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/ko_KR/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/mk/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/mk/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/nl/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/nl/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/pl/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/pl/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/pt_BR/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/pt_BR/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/ru/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/ru/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/sk/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/sk/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/sv/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/sv/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/tr/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/tr/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/uk/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/uk/LC_MESSAGES/django.po create mode 100644 rest_framework/locale/zh_CN/LC_MESSAGES/django.mo create mode 100644 rest_framework/locale/zh_CN/LC_MESSAGES/django.po (limited to 'rest_framework') diff --git a/rest_framework/locale/ar/LC_MESSAGES/django.mo b/rest_framework/locale/ar/LC_MESSAGES/django.mo new file mode 100644 index 00000000..fe1b676c Binary files /dev/null and b/rest_framework/locale/ar/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/ar/LC_MESSAGES/django.po b/rest_framework/locale/ar/LC_MESSAGES/django.po new file mode 100644 index 00000000..a910a7c9 --- /dev/null +++ b/rest_framework/locale/ar/LC_MESSAGES/django.po @@ -0,0 +1,325 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Eyad Toma , 2015 +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: Arabic (http://www.transifex.com/projects/p/django-rest-framework/language/ar/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ar\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "" + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "" + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "" + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "اسم المستخدم/كلمة السر غير صحيحين." + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "" + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "" + +#: authentication.py:168 +msgid "Invalid token." +msgstr "" + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "المستخدم غير مفعل او تم حذفه." + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "حدث خطأ في المخدم." + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "" + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "بيانات الدخول غير صحيحة." + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "لم يتم تزويد بيانات الدخول." + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "ليس لديك صلاحية للقيام بهذا الإجراء." + +#: exceptions.py:93 +msgid "Not found." +msgstr "غير موجود." + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "" + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "" + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "" + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "" + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "هذا الحقل مطلوب." + +#: fields.py:154 +msgid "This field may not be null." +msgstr "لا يمكن لهذا الحقل ان يكون فارغاً null." + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "\"{input}\" ليس قيمة منطقية." + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "لا يمكن لهذا الحقل ان يكون فارغاً." + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "تأكد ان الحقل لا يزيد عن {max_length} محرف." + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "تأكد ان الحقل {min_length} محرف على الاقل." + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "عليك ان تدخل بريد إلكتروني صالح." + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "هذه القيمة لا تطابق النمط المطلوب." + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "" + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "الرجاء إدخال رابط إلكتروني صالح." + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "الرجاء إدخال رقم صحيح صالح." + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "تأكد ان القيمة أقل أو تساوي {max_value}." + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "تأكد ان القيمة أكبر أو تساوي {min_value}." + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "" + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "الرجاء إدخال رقم صالح." + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "تأكد ان القيمة لا تحوي أكثر من {max_digits} رقم." + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "" + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "" + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "صيغة التاريخ و الوقت غير صحيحة. عليك أن تستخدم واحدة من هذه الصيغ التالية: {format}." + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "" + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "صيغة التاريخ غير صحيحة. عليك أن تستخدم واحدة من هذه الصيغ التالية: {format}." + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "" + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "صيغة الوقت غير صحيحة. عليك أن تستخدم واحدة من هذه الصيغ التالية: {format}." + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "\"{input}\" ليست واحدة من الخيارات الصالحة." + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "" + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "لم يتم إرسال أي ملف." + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "" + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "" + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "الملف الذي تم إرساله فارغ." + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "تأكد ان اسم الملف لا يحوي أكثر من {max_length} محرف (الإسم المرسل يحوي {length} محرف)." + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "رقم الصفحة \"{page_number}\" غير صالح : {message}." + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "معرف العنصر \"{pk_value}\" غير صالح - العنصر غير موجود." + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "" + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "" + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "" + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "" + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "" + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "" + +#: relations.py:296 +msgid "Invalid value." +msgstr "قيمة غير صالحة." + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "" + +#: validators.py:22 +msgid "This field must be unique." +msgstr "" + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "" + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "" + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "" + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "" + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "" + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "" + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "" + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "" + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "حساب المستخدم غير مفعل." + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "تعذر تسجيل الدخول بالبيانات التي ادخلتها." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "يجب أن تتضمن \"اسم المستخدم\" و \"كلمة المرور\"." diff --git a/rest_framework/locale/cs/LC_MESSAGES/django.mo b/rest_framework/locale/cs/LC_MESSAGES/django.mo new file mode 100644 index 00000000..a5e67713 Binary files /dev/null and b/rest_framework/locale/cs/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/cs/LC_MESSAGES/django.po b/rest_framework/locale/cs/LC_MESSAGES/django.po new file mode 100644 index 00000000..50e7034b --- /dev/null +++ b/rest_framework/locale/cs/LC_MESSAGES/django.po @@ -0,0 +1,325 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Jirka Vejrazka , 2015 +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: Czech (http://www.transifex.com/projects/p/django-rest-framework/language/cs/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: cs\n" +"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "Chybná hlavička. Nebyly poskytnuty přihlašovací údaje." + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "Chybná hlavička. Přihlašovací údaje by neměly obsahovat mezery." + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "Chybná hlavička. Přihlašovací údaje nebyly správně zakódovány pomocí base64." + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "Chybné uživatelské jméno nebo heslo." + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "Chybná hlavička tokenu. Nebyly zadány přihlašovací údaje." + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "Chybná hlavička tokenu. Přihlašovací údaje by neměly obsahovat mezery." + +#: authentication.py:168 +msgid "Invalid token." +msgstr "Chybný token." + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "Uživatelský účet je neaktivní nebo byl smazán." + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "Chyba na straně serveru." + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "Neplatný formát požadavku." + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "Chybné přihlašovací údaje." + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "Přihlašovací údaje nebyly zadány." + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "K této akci nemáte oprávnění." + +#: exceptions.py:93 +msgid "Not found." +msgstr "Nenalezeno." + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "Metoda \"{method}\" není povolena." + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "Nelze vyhovět požadavku v hlavičce Accept." + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "Nepodporovaný media type \"{media_type}\" v požadavku." + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "Pořadavek byl limitován kvůli omezení počtu požadavků za časovou periodu." + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "Toto pole je vyžadováno." + +#: fields.py:154 +msgid "This field may not be null." +msgstr "Toto pole nesmí být prázdné (null)." + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "\"{input}\" nelze použít jako typ boolean." + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "Toto pole nesmí být prázdné.." + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "Zkontrolujte, že toto pole není delší než {max_length} znaků." + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "Zkontrolujte, že toto obsahuje alespoň {min_length} znaků" + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "Vložte platnou e-mailovou adresu." + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "Hodnota v tomto poli neodpovídá požadovanému formátu." + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "Vložte platnou \"zkrácenou formu\" obsahující pouze malá písmena, čísla, spojovník nebo podtržítko." + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "Vložte platný odkaz." + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "Je vyžadováno číslo." + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "Zkontrolujte, že hodnota je menší nebo rovna {max_value}." + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "Zkontrolujte, že hodnota je větší nebo rovna {min_value}." + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "Řetězec je příliš dlouhý" + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "Je vyžadováno číslo." + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "Zkontrolujte, že číslo neobsahuje více než {max_digits} čislic." + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "Zkontrolujte, že číslo nemá více než {max_decimal_places} desetinných míst." + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "Zkontrolujte, že číslo neobsahuje více než {max_whole_digits} čislic před desetinnou čárkou." + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "Chybný formát data a času. Použijte jeden z těchto formátů: {format}." + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "Bylo zadáno pouze datum místo data a času." + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "Chybný formát data. Použijte jeden z těchto formátů: {format}." + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "Bylo zadáno datum a čas, místo samotného data." + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "Chybný formát času. Použijte jeden z těchto formátů: {format}." + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "\"{input}\" není platnou možností." + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "Byl očekáván seznam položek ale nalezen \"{input_type}\"." + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "Nebyl zaslán žádný soubor." + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "Zaslaná data neobsahují soubor. Zkontrolujte typ kódování ve formuláři." + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "Nebylo možno zjistit jméno souboru." + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "Zaslaný soubor je prázdný." + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "Zajistěte, aby jméno souboru obsahovalo maximálně {max_length} znaků (teď má {length} znaků)." + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "Nahrajte platný obrázek. Nahraný soubor buď není obrázkem, nebo je poškozen." + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "Chybné čislo stránky \"{page_number}\": {message}." + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "Chybný primární klíč \"{pk_value}\" - objekt neexistuje." + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "Chybný typ. Byl přijat typ {data_type} místo hodnoty primárního klíče." + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "Chybný odkaz - nebyla nalezena žádní shoda." + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "Chybný odkaz - byla nalezena neplatná shoda." + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "Chybný odkaz - objekt neexistuje." + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "Chybný typ. Byl přijat typ {data_type} místo očekávaného odkazu." + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "Objekt s {slug_name}={value} neexistuje." + +#: relations.py:296 +msgid "Invalid value." +msgstr "Chybná hodnota." + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "Chybná data. Byl přijat typ {datatype} místo očakávaného slovníku." + +#: validators.py:22 +msgid "This field must be unique." +msgstr "Tato položka musí být unikátní." + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "Položka {field_names} musí tvořit unikátní množinu." + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "Tato položka musí být pro datum \"{date_field}\" unikátní." + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "Tato položka musí být pro měsíc \"{date_field}\" unikátní." + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "Tato položka musí být pro rok \"{date_field}\" unikátní." + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "Chybné číslo verze v hlavičce Accept" + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "Chybné číslo verze v odkazu." + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "Chybné číslo verze v hostname." + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "Chybné čislo verze v URL parametru." + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "Uživatelský účet je zamčen." + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "Se zadanými údaji nebylo možné se přihlásit." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "Musí obsahovat \"uživatelské jméno! a \"heslo\"." diff --git a/rest_framework/locale/da/LC_MESSAGES/django.mo b/rest_framework/locale/da/LC_MESSAGES/django.mo new file mode 100644 index 00000000..f947f907 Binary files /dev/null and b/rest_framework/locale/da/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/da/LC_MESSAGES/django.po b/rest_framework/locale/da/LC_MESSAGES/django.po new file mode 100644 index 00000000..e00ffadf --- /dev/null +++ b/rest_framework/locale/da/LC_MESSAGES/django.po @@ -0,0 +1,325 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Mikkel Munch Mortensen <3xm@detfalskested.dk>, 2015 +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: Danish (http://www.transifex.com/projects/p/django-rest-framework/language/da/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: da\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "Ugyldig basic header. Ingen legitimation angivet." + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "Ugyldig basic header. Legitimationsstrenge må ikke indeholde mellemrum." + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "Ugyldig basic header. Legitimationen er ikke base64 encoded på korrekt vis." + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "Ugyldigt brugernavn/kodeord." + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "Ugyldig token header." + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "Ugyldig token header. Token-strenge må ikke indeholde mellemrum." + +#: authentication.py:168 +msgid "Invalid token." +msgstr "Ugyldigt token." + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "Inaktiv eller slettet bruger." + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "Der er sket en serverfejl." + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "Misdannet forespørgsel." + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "Ugyldig legitimation til autentificering." + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "Legitimation til autentificering blev ikke angivet." + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "Du har ikke lov til at udføre denne handling." + +#: exceptions.py:93 +msgid "Not found." +msgstr "Ikke fundet." + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "Metoden \"{method}\" er ikke tilladt." + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "Kunne ikke efterkomme forespørgslens Accept header." + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "Forespørgslens media type, \"{media_type}\", er ikke understøttet." + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "Forespørgslen blev neddroslet." + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "Dette felt er påkrævet." + +#: fields.py:154 +msgid "This field may not be null." +msgstr "Dette felt må ikke være null." + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "\"{input}\" er ikke en tilladt boolsk værdi." + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "Dette felt må ikke være tomt." + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "Tjek at dette felt ikke indeholder flere end {max_length} tegn." + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "Tjek at dette felt indeholder mindst {min_length} tegn." + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "Angiv en gyldig e-mailadresse." + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "Denne værdi passer ikke med det påkrævede mønster." + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "Indtast en gyldig \"slug\", bestående af bogstaver, tal, bund- og bindestreger." + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "Indtast en gyldig URL." + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "Et gyldigt heltal er påkrævet." + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "Tjek at værdien er mindre end eller lig med {max_value}." + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "Tjek at værdien er større end eller lig med {min_value}." + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "Strengværdien er for stor." + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "Et gyldigt tal er påkrævet." + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "Tjek at der ikke er flere end {max_digits} cifre i alt." + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "Tjek at der ikke er flere end {max_decimal_places} cifre efter kommaet." + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "Tjek at der ikke er flere end {max_whole_digits} cifre før kommaet." + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "Datotid har et forkert format. Brug i stedet et af disse formater: {format}." + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "Forventede en datotid, men fik en dato." + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "Dato har et forkert format. Brug i stedet et af disse formater: {format}." + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "Forventede en dato men fik en datotid." + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "Klokkeslæt har forkert format. Brug i stedet et af disse formater: {format}. " + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "\"{input}\" er ikke et gyldigt valg." + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "Forventede en liste, men fik noget af typen \"{input_type}\"." + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "Ingen medsendt fil." + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "Det medsendte data var ikke en fil. Tjek typen af indkodning på formularen." + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "Filnavnet kunne ikke afgøres." + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "Den medsendte fil er tom." + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "Sørg for at filnavnet er højst {max_length} langt (det er {length})." + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "Medsend et gyldigt billede. Den medsendte fil var enten ikke et billede eller billedfilen var ødelagt." + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "Ugyldig side \"{page_number}\": {message}." + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "Ugyldig primærnøgle \"{pk_value}\" - objektet findes ikke." + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "Ugyldig type. Forventet værdi er primærnøgle, fik {data_type}." + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "Ugyldigt hyperlink - intet URL match." + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "Ugyldigt hyperlink - forkert URL match." + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "Ugyldigt hyperlink - objektet findes ikke." + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "Forkert type. Forventede en URL-streng, fik {data_type}." + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "Object med {slug_name}={value} findes ikke." + +#: relations.py:296 +msgid "Invalid value." +msgstr "Ugyldig værdi." + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "Ugyldig data. Forventede en dictionary, men fik {datatype}." + +#: validators.py:22 +msgid "This field must be unique." +msgstr "Dette felt skal være unikt." + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "Felterne {field_names} skal udgøre et unikt sæt." + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "Dette felt skal være unikt for \"{date_field}\"-datoen." + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "Dette felt skal være unikt for \"{date_field}\"-måneden." + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "Dette felt skal være unikt for \"{date_field}\"-året." + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "Ugyldig version i \"Accept\" headeren." + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "Ugyldig version i URL-stien." + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "Ugyldig version i hostname." + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "Ugyldig version i forespørgselsparameteren." + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "Brugerkontoen er deaktiveret." + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "Kunne ikke logge ind med den angivne legitimation." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "Skal indeholde \"username\" og \"password\"." diff --git a/rest_framework/locale/de/LC_MESSAGES/django.mo b/rest_framework/locale/de/LC_MESSAGES/django.mo new file mode 100644 index 00000000..48245c60 Binary files /dev/null and b/rest_framework/locale/de/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/de/LC_MESSAGES/django.po b/rest_framework/locale/de/LC_MESSAGES/django.po new file mode 100644 index 00000000..74bee416 --- /dev/null +++ b/rest_framework/locale/de/LC_MESSAGES/django.po @@ -0,0 +1,325 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Thomas Tanner, 2015 +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: German (http://www.transifex.com/projects/p/django-rest-framework/language/de/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: de\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "Ungültiger basic header. Keine Zugangsdaten angegeben." + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "Ungültiger basic header. Zugangsdaten sollen keine Leerzeichen enthalten." + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "Ungültiger basic header. Zugangsdaten sind nicht korrekt mit base64 kodiert." + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "Ungültiger Benutzername/Passwort" + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "Ungültiger token header. Keine Zugangsdaten angegeben." + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "Ungültiger token header. Zugangsdaten sollen keine Leerzeichen enthalten." + +#: authentication.py:168 +msgid "Invalid token." +msgstr "Ungültiges Token" + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "Benutzer inaktiv oder gelöscht." + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "Ein Serverfehler ist aufgetreten." + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "Fehlerhafte Anfrage." + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "Falsche Anmeldedaten." + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "Anmeldedaten fehlen." + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "Sie sind nicht berechtigt, diese Aktion durchzuführen." + +#: exceptions.py:93 +msgid "Not found." +msgstr "Nicht gefunden." + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "Methode \"{method}\" nicht erlaubt." + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "Kann den Accept header der Anfrage nicht erfüllen." + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "Nicht unterstützter Medientyp \"{media_type}\" in der Anfrage." + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "Die Anfrage wurde gedrosselt." + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "Dieses Feld ist erforderlich." + +#: fields.py:154 +msgid "This field may not be null." +msgstr "Dieses Feld darf nicht Null sein." + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "\"{input}\" ist kein gültiger Boole'scher Wert." + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "Dieses Feld darf nicht leer sein." + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "Stelle sicher, dass dieses Feld nicht mehr als {max_length} Zeichen lang ist." + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "Stelle sicher, dass dieses Feld mindestens {min_length} Zeichen lang ist." + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "Gebe eine gültige E-Mail Adresse an." + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "Dieser Wert passt nicht zu dem erforderlichen Muster." + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "Gebe ein gültiges \"slug\" aus Buchstaben, Ziffern, Unterstrichen und Minuszeichen ein." + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "Gebe eine gültige URL ein." + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "Eine gültige Ganzzahl ist erforderlich." + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "Stelle sicher, dass dieser Wert kleiner oder gleich {max_value} ist." + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "Stelle sicher, dass dieser Wert größer oder gleich {max_value} ist." + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "Zeichenkette zu lang." + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "Eine gültige Zahl ist erforderlich." + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "Stelle sicher, dass es insgesamt nicht mehr als {max_digits} Ziffern lang ist." + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "Stelle sicher, dass es nicht mehr als {max_decimal_places} Nachkommastellen lang ist." + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "Stelle sicher, dass es nicht mehr als {max_whole_places} Stellen vor dem Komma lang ist." + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "Datum- und Zeitangabe hat das falsche Format. Nutze stattdessen eines dieser Formate: {format}." + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "Erwarte eine Datum- und Zeitangabe, erhielt aber ein Datum." + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "Datum hat das falsche Format. Nutze stattdessen eines dieser Formate: {format}." + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "Erwarte ein Datum, erhielt aber eine Datum- und Zeitangabe." + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "Zeitangabe hat das falsche Format. Nutze stattdessen eines dieser Formate: {format}." + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "\"{input}\" ist keine gültige Option." + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "Erwarte eine Liste von Elementen, erhielt aber den Typ \"{input_type}\"." + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "Es wurde keine Datei übermittelt." + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "Die übermittelten Daten sind keine Datei. Prüfe den Kodierungstyp im Formular." + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "Der Dateiname konnte nicht ermittelt werden." + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "Die übermittelte Datei ist leer." + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "" + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "" + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "" + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "" + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "" + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "" + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "" + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "" + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "" + +#: relations.py:296 +msgid "Invalid value." +msgstr "Ungültiger Wert." + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "Ungültige Daten. Dictionary erwartet, aber {datatype} erhalten." + +#: validators.py:22 +msgid "This field must be unique." +msgstr "Dieses Feld muss eineindeutig sein." + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "" + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "" + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "" + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "" + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "" + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "" + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "" + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "" + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "Benutzerkonto ist gesperrt." + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "Kann nicht mit den angegeben Zugangsdaten anmelden." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "\"username\" und \"password\" sind erforderlich." diff --git a/rest_framework/locale/en/LC_MESSAGES/django.mo b/rest_framework/locale/en/LC_MESSAGES/django.mo new file mode 100644 index 00000000..746915ff Binary files /dev/null and b/rest_framework/locale/en/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/en/LC_MESSAGES/django.po b/rest_framework/locale/en/LC_MESSAGES/django.po new file mode 100644 index 00000000..f3db69e5 --- /dev/null +++ b/rest_framework/locale/en/LC_MESSAGES/django.po @@ -0,0 +1,324 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: English (http://www.transifex.com/projects/p/django-rest-framework/language/en/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: en\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "Invalid basic header. No credentials provided." + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "Invalid basic header. Credentials string should not contain spaces." + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "Invalid basic header. Credentials not correctly base64 encoded." + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "Invalid username/password." + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "Invalid token header. No credentials provided." + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "Invalid token header. Token string should not contain spaces." + +#: authentication.py:168 +msgid "Invalid token." +msgstr "Invalid token." + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "User inactive or deleted." + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "A server error occurred." + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "Malformed request." + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "Incorrect authentication credentials." + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "Authentication credentials were not provided." + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "You do not have permission to perform this action." + +#: exceptions.py:93 +msgid "Not found." +msgstr "Not found." + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "Method \"{method}\" not allowed." + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "Could not satisfy the request Accept header." + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "Unsupported media type \"{media_type}\" in request." + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "Request was throttled." + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "This field is required." + +#: fields.py:154 +msgid "This field may not be null." +msgstr "This field may not be null." + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "\"{input}\" is not a valid boolean." + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "This field may not be blank." + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "Ensure this field has no more than {max_length} characters." + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "Ensure this field has at least {min_length} characters." + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "Enter a valid email address." + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "This value does not match the required pattern." + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "Enter a valid \"slug\" consisting of letters, numbers, underscores or hyphens." + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "Enter a valid URL." + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "\"{value}\" is not a valid UUID." + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "A valid integer is required." + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "Ensure this value is less than or equal to {max_value}." + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "Ensure this value is greater than or equal to {min_value}." + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "String value too large." + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "A valid number is required." + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "Ensure that there are no more than {max_digits} digits in total." + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "Ensure that there are no more than {max_decimal_places} decimal places." + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "Ensure that there are no more than {max_whole_digits} digits before the decimal point." + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "Datetime has wrong format. Use one of these formats instead: {format}." + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "Expected a datetime but got a date." + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "Date has wrong format. Use one of these formats instead: {format}." + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "Expected a date but got a datetime." + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "Time has wrong format. Use one of these formats instead: {format}." + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "\"{input}\" is not a valid choice." + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "Expected a list of items but got type \"{input_type}\"." + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "No file was submitted." + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "The submitted data was not a file. Check the encoding type on the form." + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "No filename could be determined." + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "The submitted file is empty." + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "Ensure this filename has at most {max_length} characters (it has {length})." + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "Upload a valid image. The file you uploaded was either not an image or a corrupted image." + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "Expected a dictionary of items but got type \"{input_type}\"." + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "Invalid page \"{page_number}\": {message}." + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "Invalid cursor" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "Invalid pk \"{pk_value}\" - object does not exist." + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "Incorrect type. Expected pk value, received {data_type}." + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "Invalid hyperlink - No URL match." + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "Invalid hyperlink - Incorrect URL match." + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "Invalid hyperlink - Object does not exist." + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "Incorrect type. Expected URL string, received {data_type}." + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "Object with {slug_name}={value} does not exist." + +#: relations.py:296 +msgid "Invalid value." +msgstr "Invalid value." + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "Invalid data. Expected a dictionary, but got {datatype}." + +#: validators.py:22 +msgid "This field must be unique." +msgstr "This field must be unique." + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "The fields {field_names} must make a unique set." + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "This field must be unique for the \"{date_field}\" date." + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "This field must be unique for the \"{date_field}\" month." + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "This field must be unique for the \"{date_field}\" year." + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "Invalid version in \"Accept\" header." + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "Invalid version in URL path." + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "Invalid version in hostname." + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "Invalid version in query parameter." + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "User account is disabled." + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "Unable to log in with provided credentials." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "Must include \"username\" and \"password\"." diff --git a/rest_framework/locale/en_US/LC_MESSAGES/django.mo b/rest_framework/locale/en_US/LC_MESSAGES/django.mo new file mode 100644 index 00000000..eb60d9d7 Binary files /dev/null and b/rest_framework/locale/en_US/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/en_US/LC_MESSAGES/django.po b/rest_framework/locale/en_US/LC_MESSAGES/django.po index 23f76ff7..11d94e9c 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: 2015-01-30 16:23+0000\n" +"POT-Creation-Date: 2015-01-30 16:40+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -69,7 +69,7 @@ msgstr "" msgid "You do not have permission to perform this action." msgstr "" -#: exceptions.py:93 +#: exceptions.py:93 views.py:77 msgid "Not found." msgstr "" @@ -309,6 +309,10 @@ msgstr "" msgid "Invalid version in query parameter." msgstr "" +#: views.py:81 +msgid "Permission denied." +msgstr "" + #: authtoken/serializers.py:20 msgid "User account is disabled." msgstr "" diff --git a/rest_framework/locale/es/LC_MESSAGES/django.mo b/rest_framework/locale/es/LC_MESSAGES/django.mo new file mode 100644 index 00000000..814db7be Binary files /dev/null and b/rest_framework/locale/es/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/es/LC_MESSAGES/django.po b/rest_framework/locale/es/LC_MESSAGES/django.po new file mode 100644 index 00000000..28ef893d --- /dev/null +++ b/rest_framework/locale/es/LC_MESSAGES/django.po @@ -0,0 +1,327 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# José Padilla , 2015 +# Miguel González , 2015 +# Sergio Infante , 2015 +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: Spanish (http://www.transifex.com/projects/p/django-rest-framework/language/es/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: es\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "Cabecera básica inválida. Las credenciales no fueron suministradas." + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "Cabecera básica inválida. La cadena con las credenciales no debe contener espacios." + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "Cabecera básica inválida. Las credenciales incorrectamente codificadas en base64." + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "Nombre de usuario/contraseña inválidos." + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "Cabecera token inválida. Las credenciales no fueron suministradas." + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "Cabecera token inválida. La cadena token no debe contener espacios." + +#: authentication.py:168 +msgid "Invalid token." +msgstr "Token inválido." + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "Usuario inactivo o borrado." + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "Se ha producido un error en el servidor." + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "Solicitud con formato incorrecto." + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "Credenciales de autenticación incorrectas." + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "Las credenciales de autenticación no se proveyeron." + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "Usted no tiene permiso para realizar esta acción." + +#: exceptions.py:93 +msgid "Not found." +msgstr "No encontrado." + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "Método \"{method}\" no permitido." + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "No se ha podido satisfacer la solicitud de cabecera de Accept." + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "Tipo de medio \"{media_type}\" incompatible en la solicitud." + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "Solicitud fue regulada (throttled)." + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "Este campo es requerido." + +#: fields.py:154 +msgid "This field may not be null." +msgstr "Este campo no puede ser nulo." + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "\"{input}\" no es un booleano válido." + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "Este campo no puede estar en blanco." + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "Asegúrese de que este campo no tenga más de {max_length} caracteres." + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "Asegúrese de que este campo tenga al menos {min_length} caracteres." + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "Introduzca una dirección de correo electrónico válida." + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "Este valor no coincide con el patrón requerido." + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "Introduzca un \"slug\" válido consistente en letras, números, guiones o guiones bajos." + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "Introduzca una URL válida." + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "Introduzca un número entero válido." + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "Asegúrese de que este valor es menor o igual a {max_value}." + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "Asegúrese de que este valor es mayor o igual a {min_value}." + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "Cadena demasiado larga." + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "Se requiere un número válido." + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "Asegúrese de que no haya más de {max_digits} dígitos en total." + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "Asegúrese de que no haya más de {max_decimal_places} decimales." + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "Asegúrese de que no haya más de {max_whole_digits} dígitos en la parte entera." + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "Fecha/hora con formato erróneo. Use uno de los siguientes formatos en su lugar: {format}." + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "Se esperaba un fecha/hora en vez de una fecha." + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "Fecha con formato erróneo. Use uno de los siguientes formatos en su lugar: {format}." + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "Se esperaba una fecha en vez de una fecha/hora." + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "Hora con formato erróneo. Use uno de los siguientes formatos en su lugar: {format}." + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "\"{input}\" no es una elección válida." + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "Se esperaba una lista de elementos en vez del tipo \"{input_type}\"." + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "No se envió ningún archivo." + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "La información enviada no era un archivo. Compruebe el tipo de codificación del formulario." + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "No se pudo determinar un nombre de archivo." + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "El archivo enviado está vació." + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "Asegúrese de que el nombre de archivo no tenga más de {max_length} caracteres (tiene {length})." + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "Adjunte una imagen válida. El archivo adjunto o bien no es una imagen o bien está dañado." + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "Página \"{page_number}\" inválida: {message}." + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "Clave primaria \"{pk_value}\" inválida - objeto no existe." + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "Tipo incorrecto. Se esperaba valor de clave primaria y se recibió {data_type}." + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "Hiperenlace inválido - No hay URL coincidentes." + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "Hiperenlace inválido - Coincidencia incorrecta de la URL." + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "Hiperenlace inválido - Objeto no existe." + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "Tipo incorrecto. Se esperaba una URL y se recibió {data_type}." + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "Objeto con {slug_name}={value} no existe." + +#: relations.py:296 +msgid "Invalid value." +msgstr "Valor inválido." + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "Datos inválidos. Se esperaba un diccionario pero es un {datatype}." + +#: validators.py:22 +msgid "This field must be unique." +msgstr "Este campo debe ser único." + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "Los campos {field_names} deben formar un conjunto único." + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "Este campo debe ser único para el día \"{date_field}\"." + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "Este campo debe ser único para el mes \"{date_field}\"." + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "Este campo debe ser único para el año \"{date_field}\"." + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "Versión inválida en la cabecera \"Accept\"." + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "Versión inválida en la ruta de la URL." + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "Versión inválida en el nombre de host." + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "Versión inválida en el parámetro de consulta." + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "Cuenta de usuario está deshabilitada." + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "No puede iniciar sesión con las credenciales proporcionadas." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "Debe incluir \"username\" y \"password\"." diff --git a/rest_framework/locale/et/LC_MESSAGES/django.mo b/rest_framework/locale/et/LC_MESSAGES/django.mo new file mode 100644 index 00000000..ca9b6ec4 Binary files /dev/null and b/rest_framework/locale/et/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/et/LC_MESSAGES/django.po b/rest_framework/locale/et/LC_MESSAGES/django.po new file mode 100644 index 00000000..dec03d4d --- /dev/null +++ b/rest_framework/locale/et/LC_MESSAGES/django.po @@ -0,0 +1,325 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tõnis Kärdi , 2015 +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: Estonian (http://www.transifex.com/projects/p/django-rest-framework/language/et/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: et\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "" + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "" + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "" + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "Vale kasutajatunnus/salasõna." + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "" + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "" + +#: authentication.py:168 +msgid "Invalid token." +msgstr "" + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "Kasutaja on inaktiivne või kustutatud." + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "" + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "" + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "" + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "" + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "" + +#: exceptions.py:93 +msgid "Not found." +msgstr "" + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "" + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "" + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "" + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "" + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "Väli on kohustuslik." + +#: fields.py:154 +msgid "This field may not be null." +msgstr "Väli ei tohi olla tühi." + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "" + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "" + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "" + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "" + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "Sisesta kehtiv e-posti aadress." + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "Väärtus ei ühti etteantud mustriga." + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "" + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "Sisesta korrektne URL." + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "" + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "Veendu, et väärtus on väiksem kui või võrdne väärtusega {max_value}. " + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "Veendu, et väärtus on suurem kui või võrdne väärtusega {min_value}." + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "Sõne on liiga pikk." + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "" + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "Veendu, et kokku pole rohkem kui {max_digits}." + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "Veendu, et komakohti pole rohkem kui {max_decimal_places}. " + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "" + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "" + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "" + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "" + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "" + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "" + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "" + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "" + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "" + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "" + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "" + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "" + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "" + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "" + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "" + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "" + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "" + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "" + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "" + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "" + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "" + +#: relations.py:296 +msgid "Invalid value." +msgstr "" + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "" + +#: validators.py:22 +msgid "This field must be unique." +msgstr "" + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "" + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "" + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "" + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "" + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "" + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "" + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "" + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "" + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "Kasutajakonto on suletud" + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "Sisselogimine antud tunnusega ebaõnnestus." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "Peab sisaldama \"kasutajatunnust\" ja \"slasõna\"." diff --git a/rest_framework/locale/fr/LC_MESSAGES/django.mo b/rest_framework/locale/fr/LC_MESSAGES/django.mo new file mode 100644 index 00000000..68519d45 Binary files /dev/null and b/rest_framework/locale/fr/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/fr/LC_MESSAGES/django.po b/rest_framework/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 00000000..e8597c30 --- /dev/null +++ b/rest_framework/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,326 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Etienne Desgagné , 2015 +# Martin Maillard , 2015 +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: French (http://www.transifex.com/projects/p/django-rest-framework/language/fr/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: fr\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "En-tête « basic » non valide. Informations d'identification non fournies." + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "En-tête « basic » non valide. Les informations d'identification ne doivent pas contenir d'espaces." + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "En-tête « basic » non valide. Encodage base64 des informations d'identification incorrect." + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "Nom d'utilisateur et/ou mot de passe non valide(s)." + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "En-tête « token » non valide. Informations d'identification non fournies." + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "En-tête « token » non valide. Un token ne doit pas contenir d'espaces." + +#: authentication.py:168 +msgid "Invalid token." +msgstr "Token non valide." + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "Utilisateur inactif ou supprimé." + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "Une erreur du serveur est survenue." + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "Requête malformée" + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "Informations d'authentification incorrectes." + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "Informations d'authentification non fournies." + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "Vous n'avez pas la permission d'effectuer cette action." + +#: exceptions.py:93 +msgid "Not found." +msgstr "" + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "Méthode \"{method}\" non autorisée." + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "" + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "" + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "" + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "Ce champ est obligatoire." + +#: fields.py:154 +msgid "This field may not be null." +msgstr "Ce champ ne peut être null." + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "\"{input}\" n'est pas un booléen valide." + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "Ce champ ne peut être vide." + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "Assurez-vous que ce champ comporte au plus {max_length} caractères." + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "Assurez-vous que ce champ comporte au moins {min_length} caractères." + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "Saisissez une adresse email valable." + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "Cette valeur ne satisfait pas le motif imposé." + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "Ce champ ne doit contenir que des lettres, des nombres, des tirets bas _ et des traits d'union." + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "Saisissez une URL valide." + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "Saisissez un nombre entier valide." + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "Assurez-vous que cette valeur est inférieure ou égale à {max_value}." + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "Assurez-vous que cette valeur est supérieure ou égale à {min_value}." + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "Chaîne de caractères trop longue." + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "Un nombre valide est requis." + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "Assurez-vous qu'il n'y a pas plus de {max_digits} chiffres au total." + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "Assurez-vous qu'il n'y a pas plus de {max_decimal_places} chiffres après la virgule." + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "Assurez-vous qu'il n'y a pas plus de {max_whole_digits} chiffres avant la virgule." + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "" + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "" + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "" + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "" + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "" + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "\"{input}\" n'est pas un choix valide." + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "" + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "Aucun fichier n'a été soumis." + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "La donnée soumise n'est pas un fichier. Vérifiez le type d'encodage du formulaire." + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "Le nom de fichier n'a pu être déterminé." + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "Le fichier soumis est vide." + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "Assurez-vous que le nom de fichier comporte au plus {max_length} caractères (il en comporte {length})." + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "Transférez une image valide. Le fichier que vous avez transféré n'est pas une image, ou il est corrompu." + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "Page \"{page_number}\" non valide : {message}." + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "Clé primaire \"{pk_value}\" non valide - l'objet n'existe pas." + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "" + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "" + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "" + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "" + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "" + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "L'object avec {slug_name}={value} n'existe pas." + +#: relations.py:296 +msgid "Invalid value." +msgstr "Valeur non valide." + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "" + +#: validators.py:22 +msgid "This field must be unique." +msgstr "Ce champ doit être unique." + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "Les champs {field_names} doivent former un ensemble unique." + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "Ce champ doit être unique pour la date \"{date_field}\"." + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "Ce champ doit être unique pour le mois \"{date_field}\"." + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "Ce champ doit être unique pour l'année \"{date_field}\"." + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "Version non valide dans l'en-tête « Accept »." + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "Version non valide dans l'URL." + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "Version non valide dans le nom d'hôte." + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "Version non valide dans le paramètre de requête." + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "Ce compte est désactivé." + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "Impossible de se connecter avec les informations d'identification fournies." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "\"username\" et \"password\" doivent être inclus." diff --git a/rest_framework/locale/hu/LC_MESSAGES/django.mo b/rest_framework/locale/hu/LC_MESSAGES/django.mo new file mode 100644 index 00000000..451b0b9a Binary files /dev/null and b/rest_framework/locale/hu/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/hu/LC_MESSAGES/django.po b/rest_framework/locale/hu/LC_MESSAGES/django.po new file mode 100644 index 00000000..14fb6544 --- /dev/null +++ b/rest_framework/locale/hu/LC_MESSAGES/django.po @@ -0,0 +1,325 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Zoltan Szalai , 2015 +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: Hungarian (http://www.transifex.com/projects/p/django-rest-framework/language/hu/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: hu\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "Érvénytelen basic fejlécmező. Nem voltak megadva azonosítók." + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "Érvénytelen basic fejlécmező. Az azonosító karakterlánc nem tartalmazhat szóközöket." + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "Érvénytelen basic fejlécmező. Az azonosítók base64 kódolása nem megfelelő." + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "Érvénytelen felhasználónév/jelszó." + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "Érvénytelen token fejlécmező. Nem voltak megadva azonosítók." + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "Érvénytelen token fejlécmező. A token karakterlánc nem tartalmazhat szóközöket." + +#: authentication.py:168 +msgid "Invalid token." +msgstr "Érvénytelen token." + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "A felhasználó nincs aktiválva vagy törölve lett." + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "Szerver oldali hiba történt." + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "Hibás kérés." + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "Hibás azonosítók." + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "Nem voltak megadva azonosítók." + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "Nincs jogosultsága a művelet végrehajtásához." + +#: exceptions.py:93 +msgid "Not found." +msgstr "Nem található." + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "A \"{method}\" metódus nem megengedett." + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "A kérés Accept fejlécmezőjét nem lehetett kiszolgálni." + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "Nem támogatott média típus \"{media_type}\" a kérésben." + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "A kérés korlátozva lett." + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "Ennek a mezőnek a megadása kötelező." + +#: fields.py:154 +msgid "This field may not be null." +msgstr "Ez a mező nem lehet null értékű." + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "Az \"{input}\" nem egy érvényes logikai érték." + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "Ez a mező nem lehet üres." + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "Bizonyosodjon meg arról, hogy ez a mező legfeljebb {max_length} karakterből áll." + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "Bizonyosodjon meg arról, hogy ez a mező legalább {min_length} karakterből áll." + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "Adjon meg egy érvényes e-mail címet!" + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "Ez az érték nem illeszkedik a szükséges mintázatra." + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "Az URL barát cím csak betűket, számokat, aláhúzásokat és kötőjeleket tartalmazhat." + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "Adjon meg egy érvényes URL-t!" + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "Egy érvényes egész szám megadása szükséges." + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "Bizonyosodjon meg arról, hogy ez az érték legfeljebb {max_value}." + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "Bizonyosodjon meg arról, hogy ez az érték legalább {min_value}." + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "A karakterlánc túl hosszú." + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "Egy érvényes szám megadása szükséges." + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "Bizonyosodjon meg arról, hogy a számjegyek száma összesen legfeljebb {max_digits}." + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "Bizonyosodjon meg arról, hogy a tizedes tört törtrészében levő számjegyek száma összesen legfeljebb {max_decimal_places}." + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "Bizonyosodjon meg arról, hogy a tizedes tört egész részében levő számjegyek száma összesen legfeljebb {max_whole_digits}." + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "A dátum formátuma hibás. Használja ezek valamelyikét helyette: {format}." + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "Időt is tartalmazó dátum helyett egy időt nem tartalmazó dátum lett elküldve." + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "A dátum formátuma hibás. Használja ezek valamelyikét helyette: {format}." + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "Időt nem tartalmazó dátum helyett egy időt is tartalmazó dátum lett elküldve." + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "Az idő formátuma hibás. Használja ezek valamelyikét helyette: {format}." + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "Az \"{input}\" nem egy érvényes elem." + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "Elemek listája helyett \"{input_type}\" lett elküldve." + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "Semmilyen fájl sem került feltöltésre." + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "Az elküldött adat nem egy fájl volt. Ellenőrizze a kódolás típusát az űrlapon!" + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "A fájlnév nem megállapítható." + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "A küldött fájl üres." + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "Bizonyosodjon meg arról, hogy a fájlnév legfeljebb {max_length} karakterből áll (jelenlegi hossza: {length})." + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "Töltsön fel egy érvényes képfájlt! A feltöltött fájl nem kép volt, vagy megsérült." + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "Érvénytelen oldal \"{page_number}\": {message}." + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "Érvénytelen pk \"{pk_value}\" - az objektum nem létezik." + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "Helytelen típus. pk érték helyett {data_type} lett elküldve." + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "Érvénytelen link - Nem illeszkedő URL." + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "Érvénytelen link. - Eltérő URL illeszkedés." + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "Érvénytelen link - Az objektum nem létezik." + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "Helytelen típus. URL karakterlánc helyett {data_type} lett elküldve." + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "Nem létezik olyan objektum, amelynél {slug_name}={value}." + +#: relations.py:296 +msgid "Invalid value." +msgstr "Érvénytelen érték." + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "Érvénytelen adat. Egy dictionary helyett {datatype} lett elküldve." + +#: validators.py:22 +msgid "This field must be unique." +msgstr "Ennek a mezőnek egyedinek kell lennie." + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "A {field_names} mezőnevek nem tartalmazhatnak duplikátumot." + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "A mezőnek egyedinek kell lennie a \"{date_field}\" dátumra." + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "A mezőnek egyedinek kell lennie a \"{date_field}\" hónapra." + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "A mezőnek egyedinek kell lennie a \"{date_field}\" évre." + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "Érvénytelen verzió az \"Accept\" fejlécmezőben." + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "Érvénytelen verzió az URL elérési útban." + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "Érvénytelen verzió a hosztnévben." + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "Érvénytelen verzió a lekérdezési paraméterben." + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "A felhasználó tiltva van." + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "A megadott azonosítókkal nem lehet bejelentkezni." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "Tartalmaznia kell a \"felhasználónevet\" és a \"jelszót\"." diff --git a/rest_framework/locale/id/LC_MESSAGES/django.mo b/rest_framework/locale/id/LC_MESSAGES/django.mo new file mode 100644 index 00000000..7fc98bda Binary files /dev/null and b/rest_framework/locale/id/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/id/LC_MESSAGES/django.po b/rest_framework/locale/id/LC_MESSAGES/django.po new file mode 100644 index 00000000..99b70546 --- /dev/null +++ b/rest_framework/locale/id/LC_MESSAGES/django.po @@ -0,0 +1,324 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: Indonesian (http://www.transifex.com/projects/p/django-rest-framework/language/id/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: id\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "" + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "" + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "" + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "" + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "" + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "" + +#: authentication.py:168 +msgid "Invalid token." +msgstr "" + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "" + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "" + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "" + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "" + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "" + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "" + +#: exceptions.py:93 +msgid "Not found." +msgstr "" + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "" + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "" + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "" + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "" + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "" + +#: fields.py:154 +msgid "This field may not be null." +msgstr "" + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "" + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "" + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "" + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "" + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "" + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "" + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "" + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "" + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "" + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "" + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "" + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "" + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "" + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "" + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "" + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "" + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "" + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "" + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "" + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "" + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "" + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "" + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "" + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "" + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "" + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "" + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "" + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "" + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "" + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "" + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "" + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "" + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "" + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "" + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "" + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "" + +#: relations.py:296 +msgid "Invalid value." +msgstr "" + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "" + +#: validators.py:22 +msgid "This field must be unique." +msgstr "" + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "" + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "" + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "" + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "" + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "" + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "" + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "" + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "" + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "" + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "" + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "" diff --git a/rest_framework/locale/it/LC_MESSAGES/django.mo b/rest_framework/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 00000000..82ceb810 Binary files /dev/null and b/rest_framework/locale/it/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/it/LC_MESSAGES/django.po b/rest_framework/locale/it/LC_MESSAGES/django.po new file mode 100644 index 00000000..2cdfb877 --- /dev/null +++ b/rest_framework/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,325 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Mattia Procopio , 2015 +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: Italian (http://www.transifex.com/projects/p/django-rest-framework/language/it/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: it\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "" + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "" + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "" + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "Nome utente/password non validi" + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "Header del token non valido. Credenziali non fornite." + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "Header del token non valido. Il contenuto del token non dovrebbe contenere spazi." + +#: authentication.py:168 +msgid "Invalid token." +msgstr "Token invalido." + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "Utente inattivo o eliminato." + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "Errore del server." + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "Richiesta malformata." + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "Credenziali di autenticazione incorrette." + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "Non sono state immesse le credenziali di autenticazione." + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "Non hai l'autorizzazione per eseguire questa azione." + +#: exceptions.py:93 +msgid "Not found." +msgstr "Non trovato." + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "Metodo \"{method}\" non consentito" + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "Impossibile soddisfare l'header \"Accept\" presente nella richiesta." + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "Tipo di media \"{media_type}\"non supportato." + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "" + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "Campo obbligatorio." + +#: fields.py:154 +msgid "This field may not be null." +msgstr "Il campo non puà essere nullo." + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "\"{input}\" non è un valido valore booleano." + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "Questo campo non può essere omesso." + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "Assicurati che questo campo non abbia più di {max_length} caratteri." + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "Assicurati che questo campo abbia almeno {max_length} caratteri." + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "Inserisci un indirizzo email valido." + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "" + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "Immetti uno \"slug\" valido che consista di lettere, numeri, underscore o trattini." + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "Inserisci un URL valido" + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "È richiesto un numero intero valido." + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "Assicurati che il valore sia minore o uguale a {max_value}." + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "Assicurati che il valore sia maggiore o uguale a {min_value}." + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "" + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "È richiesto un numero valido." + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "Assicurati che non ci siano più di {max_digits} cifre in totale." + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "Assicurati che non ci siano più di {max_decimal_places} cifre decimali." + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "Assicurati che non ci siano più di {max_whole_digits} cifre prima del separatore decimale." + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "L'oggetto di tipo datetime è in un formato errato. Usa uno dei seguenti formati: {format}." + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "Atteso un oggetto di tipo datetime ma l'oggetto ricevuto è di tipo date." + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "La data è in un formato errato. Usa uno dei seguenti formati: {format}." + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "Atteso un oggetto di tipo date ma l'oggetto ricevuto è di tipo datetime." + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "" + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "\"{input}\" non è una scelta valida." + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "Attesa una lista di oggetti ma l'oggetto ricevuto è di tipo \"{input_type}\"." + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "Non è stato inviato alcun file." + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "" + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "Il nome del file non può essere determinato." + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "Il file inviato è vuoto." + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "" + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "" + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "" + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "" + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "" + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "" + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "" + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "" + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "" + +#: relations.py:296 +msgid "Invalid value." +msgstr "Valore non valido." + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "" + +#: validators.py:22 +msgid "This field must be unique." +msgstr "Questo campo deve essere unico." + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "" + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "" + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "" + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "" + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "" + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "" + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "" + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "" + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "L'account dell'utente è disabilitato" + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "Impossibile eseguire il log in con le credenziali immesse." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "Deve includere \"nome utente\" e \"password\"." diff --git a/rest_framework/locale/ko_KR/LC_MESSAGES/django.mo b/rest_framework/locale/ko_KR/LC_MESSAGES/django.mo new file mode 100644 index 00000000..457bb53c Binary files /dev/null and b/rest_framework/locale/ko_KR/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/ko_KR/LC_MESSAGES/django.po b/rest_framework/locale/ko_KR/LC_MESSAGES/django.po new file mode 100644 index 00000000..963fe89a --- /dev/null +++ b/rest_framework/locale/ko_KR/LC_MESSAGES/django.po @@ -0,0 +1,325 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# SUN CHOI , 2015 +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: Korean (Korea) (http://www.transifex.com/projects/p/django-rest-framework/language/ko_KR/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ko_KR\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "기본 헤더(basic header)가 유효하지 않습니다. 인증데이터(credentials)가 제공되지 않았습니다." + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "기본 헤더(basic header)가 유효하지 않습니다. 인증데이터(credentials) 문자열은 빈칸(spaces)을 포함하지 않아야 합니다." + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "기본 헤더(basic header)가 유효하지 않습니다. 인증데이터(credentials)가 base64로 적절히 부호화(encode)되지 않았습니다." + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "아이디/비밀번호가 유효하지 않습니다." + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "토큰 헤더가 유효하지 않습니다. 인증데이터(credentials)가 제공되지 않았습니다." + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "토큰 헤더가 유효하지 않습니다. 토큰 문자열은 빈칸(spaces)를 포함하지 않아야 합니다." + +#: authentication.py:168 +msgid "Invalid token." +msgstr "토큰이 유효하지 않습니다." + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "계정이 중지되었거나 삭제되었습니다." + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "서버 장애가 발생했습니다." + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "잘못된 요청입니다." + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "자격 인증데이터(authentication credentials)가 정확하지 않습니다." + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "자격 인증데이터(authentication credentials)가 제공되지 않았습니다." + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "이 작업을 " + +#: exceptions.py:93 +msgid "Not found." +msgstr "찾을 수 없습니다." + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "메소드(Method) \"{method}\"는 허용되지 않습니다." + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "" + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "요청된 \"{media_type}\"가 지원되지 않는 미디어 형태입니다." + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "요청이 지연(throttled)되었습니다." + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "이 항목을 채워주십시오." + +#: fields.py:154 +msgid "This field may not be null." +msgstr "" + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "\"{input}\"이 유효하지 않은 부울(boolean)입니다." + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "" + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "이 칸이 글자 수가 {max_length} 이하인지 확인하십시오." + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "이 칸이 글자 수가 적어도 {min_length} 이상인지 확인하십시오." + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "유효한 이메일 주소를 입력하십시오." + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "형식에 맞지 않는 값입니다." + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "문자, 숫자, 밑줄( _ ) 또는 하이픈( - )으로 이루어진 유효한 \"slug\"를 입력하십시오." + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "유효한 URL을 입력하십시오." + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "유효한 정수(integer)를 넣어주세요." + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "이 값이 {max_value}보다 작거나 같은지 확인하십시오." + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "이 값이 {min_value}보다 크거나 같은지 확인하십시오." + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "문자열 값이 너무 큽니다." + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "유효한 숫자를 넣어주세요." + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "전체 숫자(digits)가 {max_digits} 이하인지 확인하십시오." + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "소수점 자릿수가 {max_decimal_places} 이하인지 확인하십시오." + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "소수점 자리 앞에 숫자(digits)가 {max_whole_digits} 이하인지 확인하십시오." + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "Datetime의 포멧이 잘못되었습니다. 이 형식들 중 한가지를 사용하세요: {format}." + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "예상된 datatime 대신 date를 받았습니다." + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "Date의 포멧이 잘못되었습니다. 이 형식들 중 한가지를 사용하세요: {format}." + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "예상된 date 대신 datetime을 받았습니다." + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "Time의 포멧이 잘못되었습니다. 이 형식들 중 한가지를 사용하세요: {format}." + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "\"{input}\"이 유효하지 않은 선택(choice)입니다." + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "아이템 리스트가 예상되었으나 \"{input_type}\"를 받았습니다." + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "파일이 제출되지 않았습니다." + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "제출된 데이터는 파일이 아닙니다. 제출된 서식의 인코딩 형식을 확인하세요." + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "파일명을 알 수 없습니다." + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "제출된 파일이 비어있습니다." + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "이 파일명의 글자수가 최대 {max_length}를 넘지 않는지 확인하십시오. (이것은 {length}가 있습니다)." + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "유효한 이미지 파일을 업로드 하십시오. 업로드 하신 파일은 이미지 파일이 아니거나 손상된 이미지 파일입니다." + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "유효하지 않은 page \"{page_number}\": {message}." + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "유효하지 않은 pk \"{pk_value}\" - 객체가 존재하지 않습니다." + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "잘못된 형식입니다. pk 값 대신 {data_type}를 받았습니다." + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "유효하지 않은 하이퍼링크 - 일치하는 URL이 없습니다." + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "유효하지 않은 하이퍼링크 - URL이 일치하지 않습니다." + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "유효하지 않은 하이퍼링크 - 객체가 존재하지 않습니다." + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "잘못된 형식입니다. URL 문자열을 예상했으나 {data_type}을 받았습니다." + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "{slug_name}={value} 객체가 존재하지 않습니다." + +#: relations.py:296 +msgid "Invalid value." +msgstr "값이 유효하지 않습니다." + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "유효하지 않은 데이터. 딕셔너리(dictionary)대신 {datatype}를 받았습니다." + +#: validators.py:22 +msgid "This field must be unique." +msgstr "" + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "" + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "" + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "" + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "" + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "" + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "" + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "" + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "" + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "사용자 계정을 사용할 수 없습니다." + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "제공된 인증데이터(credentials)로는 로그인할 수 없습니다." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "\"아이디\"와 \"비밀번호\"를 포함해야 합니다." diff --git a/rest_framework/locale/mk/LC_MESSAGES/django.mo b/rest_framework/locale/mk/LC_MESSAGES/django.mo new file mode 100644 index 00000000..fc586626 Binary files /dev/null and b/rest_framework/locale/mk/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/mk/LC_MESSAGES/django.po b/rest_framework/locale/mk/LC_MESSAGES/django.po new file mode 100644 index 00000000..d9a46953 --- /dev/null +++ b/rest_framework/locale/mk/LC_MESSAGES/django.po @@ -0,0 +1,325 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Filip Dimitrovski , 2015 +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: Macedonian (http://www.transifex.com/projects/p/django-rest-framework/language/mk/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: mk\n" +"Plural-Forms: nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "Невалиден основен header. Не се внесени податоци за автентикација." + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "Невалиден основен header. Автентикационата низа не треба да содржи празни места." + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "Невалиден основен header. Податоците за автентикација не се енкодирани со base64." + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "Невалидно корисничко име/лозинка." + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "Невалиден токен header. Не се внесени податоци за најава." + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "Невалиден токен во header. Токенот не треба да содржи празни места." + +#: authentication.py:168 +msgid "Invalid token." +msgstr "Невалиден токен." + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "Корисникот е деактивиран или избришан." + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "Настана серверска грешка." + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "Неправилен request." + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "Неточни податоци за најава." + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "Не се внесени податоци за најава." + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "Немате дозвола да го сторите ова." + +#: exceptions.py:93 +msgid "Not found." +msgstr "Не е пронајдено ништо." + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "Методата \"{method}\" не е дозволена." + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "Не може да се исполни барањето на Accept header-от." + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "Media типот „{media_type}“ не е поддржан." + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "Request-от е забранет заради ограничувања." + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "Ова поле е задолжително." + +#: fields.py:154 +msgid "This field may not be null." +msgstr "Ова поле не смее да биде недефинирано." + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "\"{input}\" не е валиден boolean." + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "Ова поле не смее да биде празно." + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "Ова поле не смее да има повеќе од {max_length} знаци." + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "Ова поле мора да има барем {min_length} знаци." + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "Внесете валидна email адреса." + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "Ова поле не е по правилната шема/барање." + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "Внесете валидно име што содржи букви, бројки, долни црти или црти." + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "Внесете валиден URL." + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "Задолжителен е валиден цел број." + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "Вредноста треба да биде помала или еднаква на {max_value}." + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "Вредноста треба да биде поголема или еднаква на {min_value}." + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "Вредноста е преголема." + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "Задолжителен е валиден број." + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "Не смее да има повеќе од {max_digits} цифри вкупно." + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "Не смее да има повеќе од {max_decimal_places} децимални места." + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "Не смее да има повеќе од {max_whole_digits} цифри пред децималната точка." + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "Датата и времето се со погрешен формат. Користете го овој формат: {format}." + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "Очекувано беше дата и време, а внесено беше само дата." + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "Датата е со погрешен формат. Користете го овој формат: {format}." + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "Очекувана беше дата, а внесени беа и дата и време." + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "Времето е со погрешен формат. Користете го овој формат: {format}." + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "„{input}“ не е валиден избор." + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "Очекувана беше листа, а внесено беше „{input_type}“." + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "Ниеден фајл не е качен (upload-иран)." + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "Испратените податоци не се фајл. Проверете го encoding-от на формата." + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "Не може да се открие име на фајлот." + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "Качениот (upload-иран) фајл е празен." + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "Името на фајлот треба да има највеќе {max_length} знаци (а има {length})." + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "Качете (upload-ирајте) валидна слика. Фајлот што го качивте не е валидна слика или е расипан." + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "Невалидна страна „{page_number}“: {message}." + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "Невалиден pk „{pk_value}“ - објектот не постои." + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "Неточен тип. Очекувано беше pk, а внесено {data_type}." + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "Невалиден хиперлинк - не е внесен URL." + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "Невалиден хиперлинк - внесен е неправилен URL." + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "Невалиден хиперлинк - Објектот не постои." + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "Неточен тип. Очекувано беше URL, a внесено {data_type}." + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "Објектот со {slug_name}={value} не постои." + +#: relations.py:296 +msgid "Invalid value." +msgstr "Невалидна вредност." + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "Невалидни податоци. Очекуван беше dictionary, а внесен {datatype}." + +#: validators.py:22 +msgid "This field must be unique." +msgstr "Ова поле мора да биде уникатно." + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "Полињата {field_names} заедно мора да формираат уникатен збир." + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "Ова поле мора да биде уникатно за „{date_field}“ датата." + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "Ова поле мора да биде уникатно за „{date_field}“ месецот." + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "Ова поле мора да биде уникатно за „{date_field}“ годината." + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "Невалидна верзија во „Accept“ header-от." + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "Невалидна верзија во URL патеката." + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "Невалидна верзија во hostname-от." + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "Невалидна верзија во query параметарот." + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "Сметката на корисникот е деактивирана." + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "Не може да се најавите со податоците за најава." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "Мора да се внесе „username“ и „password“." diff --git a/rest_framework/locale/nl/LC_MESSAGES/django.mo b/rest_framework/locale/nl/LC_MESSAGES/django.mo new file mode 100644 index 00000000..b0e1ad77 Binary files /dev/null and b/rest_framework/locale/nl/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/nl/LC_MESSAGES/django.po b/rest_framework/locale/nl/LC_MESSAGES/django.po new file mode 100644 index 00000000..a1215512 --- /dev/null +++ b/rest_framework/locale/nl/LC_MESSAGES/django.po @@ -0,0 +1,324 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: Dutch (http://www.transifex.com/projects/p/django-rest-framework/language/nl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: nl\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "" + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "" + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "" + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "" + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "" + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "" + +#: authentication.py:168 +msgid "Invalid token." +msgstr "" + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "" + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "" + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "" + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "" + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "" + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "" + +#: exceptions.py:93 +msgid "Not found." +msgstr "" + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "" + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "" + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "" + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "" + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "" + +#: fields.py:154 +msgid "This field may not be null." +msgstr "" + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "" + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "" + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "" + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "" + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "" + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "" + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "" + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "" + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "" + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "" + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "" + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "" + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "" + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "" + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "" + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "" + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "" + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "" + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "" + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "" + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "" + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "" + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "" + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "" + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "" + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "" + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "" + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "" + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "" + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "" + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "" + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "" + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "" + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "" + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "" + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "" + +#: relations.py:296 +msgid "Invalid value." +msgstr "" + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "" + +#: validators.py:22 +msgid "This field must be unique." +msgstr "" + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "" + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "" + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "" + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "" + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "" + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "" + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "" + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "" + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "" + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "" + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "" diff --git a/rest_framework/locale/pl/LC_MESSAGES/django.mo b/rest_framework/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 00000000..9db72cfb Binary files /dev/null and b/rest_framework/locale/pl/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/pl/LC_MESSAGES/django.po b/rest_framework/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 00000000..8e51d754 --- /dev/null +++ b/rest_framework/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,326 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Janusz Harkot , 2015 +# Maciek Olko , 2015 +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: Polish (http://www.transifex.com/projects/p/django-rest-framework/language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "Niepoprawny podstawowy nagłówek. Brak danych uwierzytelniających." + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "Niepoprawny podstawowy nagłówek. Ciąg znaków danych uwierzytelniających nie powinien zawierać spacji." + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "Niepoprawny podstawowy nagłówek. Niewłaściwe kodowanie base64 danych uwierzytelniających." + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "Niepoprawna nazwa użytkownika lub hasło." + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "Niepoprawny nagłówek tokena. Brak danych uwierzytelniających." + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "Niepoprawny nagłówek tokena. Token nie może zawierać odstępów." + +#: authentication.py:168 +msgid "Invalid token." +msgstr "Niepoprawny token." + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "Użytkownik nieaktywny lub usunięty." + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "Wystąpił błąd serwera." + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "Zniekształcone żądanie." + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "Błędne dane uwierzytelniające." + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "Nie podano danych uwierzytelniających." + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "Nie masz uprawnień, by wykonać tę czynność." + +#: exceptions.py:93 +msgid "Not found." +msgstr "Nie znaleziono." + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "Niedozwolona metoda \"{method}\"." + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "Nie można zaspokoić nagłówka Accept żądania." + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "Brak wsparcia dla żądanego typu danych \"{media_type}\"." + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "Żądanie zostało zdławione." + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "To pole jest wymagane." + +#: fields.py:154 +msgid "This field may not be null." +msgstr "Pole nie może mieć wartości null." + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "\"{input}\" nie jest poprawną wartością logiczną." + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "To pole nie może być puste." + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "Upewnij się, że to pole ma nie więcej niż {max_length} znaków." + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "Upewnij się, że pole ma co najmniej {min_length} znaków." + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "Podaj poprawny adres e-mail." + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "Ta wartość nie pasuje do wymaganego wzorca." + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "Wprowadź poprawną wartość pola typu \"slug\", składającą się ze znaków łacińskich, cyfr, podkreślenia lub myślnika." + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "Wprowadź poprawny adres URL." + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "Wymagana poprawna liczba całkowita." + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "Upewnij się, że ta wartość jest mniejsza lub równa {max_value}." + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "Upewnij się, że ta wartość jest większa lub równa {min_value}." + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "Za długi ciąg znaków." + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "Wymagana poprawna liczba." + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "Upewnij się, że liczba ma nie więcej niż {max_digits} cyfr." + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "Upewnij się, że liczba ma nie więcej niż {max_decimal_places} cyfr dziesiętnych." + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "Upewnij się, że liczba ma nie więcej niż {max_whole_digits} cyfr całkowitych." + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "Wartość daty z czasem ma zły format. Użyj jednego z dostępnych formatów: {format}." + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "Oczekiwano datę z czasem, otrzymano tylko datę." + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "Data ma zły format. Użyj jednego z tych formatów: {format}." + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "Oczekiwano daty a otrzymano datę z czasem." + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "Błędny format czasu. Użyj jednego z dostępnych formatów: {format}" + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "\"{input}\" nie jest poprawnym wyborem." + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "Oczekiwano listy elementów, a otrzymano dane typu \"{input_type}\"." + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "Nie przesłano pliku." + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "Przesłane dane nie były plikiem. Sprawdź typ kodowania formatki." + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "Nie można określić nazwy pliku." + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "Przesłany plik jest pusty." + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "Upewnij się, że nazwa pliku ma długość co najwyżej {max_length} znaków (aktualnie ma {length})." + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "Prześlij poprawny plik graficzny. Przesłany plik albo nie jest grafiką lub jest uszkodzony." + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "Niepoprawna strona \"{page_number}\": {message}." + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "Błędny klucz główny \"{pk_value}\" - obiekt nie istnieje." + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "Błędny typ danych. Oczekiwano wartość klucza głównego, otrzymano {data_type}." + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "Błędny hyperlink - nie znaleziono pasującego adresu URL." + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "Błędny hyperlink - błędne dopasowanie adresu URL." + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "Błędny hyperlink - obiekt nie istnieje." + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "Błędny typ danych. Oczekiwano adresu URL, otrzymano {data_type}" + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "Obiekt z polem {slug_name}={value} nie istnieje" + +#: relations.py:296 +msgid "Invalid value." +msgstr "Niepoprawna wartość." + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "Niepoprawne dane. Oczekiwano słownika, otrzymano {datatype}." + +#: validators.py:22 +msgid "This field must be unique." +msgstr "Wartość dla tego pola musi być unikalna." + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "Pola {field_names} muszą tworzyć unikalny zestaw." + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "To pole musi mieć unikalną wartość dla jednej daty z pola \"{date_field}\"." + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "To pole musi mieć unikalną wartość dla konkretnego miesiąca z pola \"{date_field}\"." + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "To pole musi mieć unikalną wartość dla konkretnego roku z pola \"{date_field}\"." + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "Błędna wersja w nagłówku \"Accept\"." + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "Błędna wersja w ścieżce URL." + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "Błędna wersja w nazwie hosta." + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "Błędna wersja w parametrach zapytania." + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "Konto użytkownika jest nieaktywne." + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "Podane dane uwierzytelniające nie pozwalają na zalogowanie." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "Musi zawierać \"username\" i \"password\"." diff --git a/rest_framework/locale/pt_BR/LC_MESSAGES/django.mo b/rest_framework/locale/pt_BR/LC_MESSAGES/django.mo new file mode 100644 index 00000000..49f01929 Binary files /dev/null and b/rest_framework/locale/pt_BR/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/pt_BR/LC_MESSAGES/django.po b/rest_framework/locale/pt_BR/LC_MESSAGES/django.po new file mode 100644 index 00000000..3f272f71 --- /dev/null +++ b/rest_framework/locale/pt_BR/LC_MESSAGES/django.po @@ -0,0 +1,326 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Craig Blaszczyk , 2015 +# Filipe Rinaldi , 2015 +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/django-rest-framework/language/pt_BR/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pt_BR\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "Cabeçalho básico inválido. Credenciais não fornecidas." + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "Cabeçalho básico inválido. String de credenciais não deve incluir espaços." + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "Cabeçalho básico inválido. Credenciais codificadas em base64 incorretamente." + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "Usário ou senha inválido." + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "Cabeçalho de token inválido. Credenciais não fornecidas." + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "Cabeçalho de token inválido. String de token não deve incluir espaços." + +#: authentication.py:168 +msgid "Invalid token." +msgstr "Token inválido." + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "Usuário inativo ou removido." + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "Ocorreu um erro de servidor." + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "Pedido malformado." + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "Credenciais de autenticação incorretas." + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "As credenciais de autenticação não foram fornecidas." + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "Você não tem persmissao para executar essa ação." + +#: exceptions.py:93 +msgid "Not found." +msgstr "Não encontrado." + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "Método \"{method}\" não é permitido." + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "Não foi possível satisfazer a requisição do cabeçalho Accept." + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "Media type \"{media_type}\" no pedido não é suportado." + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "Pedido foi limitado." + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "Este campo é obrigatório." + +#: fields.py:154 +msgid "This field may not be null." +msgstr "Este campo não pode ser nulo." + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "\"{input}\" não é um valor boleano válido." + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "Este campo não pode ser em branco." + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "Certifique-se de que este campo não tenha mais de {max_length} caracteres." + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "Certifique-se de que este campo tenha mais de {min_length} caracteres." + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "Insira um endereço de email válido." + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "Este valor não corresponde ao padrão exigido." + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "Entrar um \"slug\" válido que consista de letras, números, sublinhados ou hífens." + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "Entrar um URL válido." + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "Um número inteiro válido é exigido." + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "Certifique-se de que este valor seja inferior ou igual a {max_value}." + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "Certifque-se de que este valor seja maior ou igual a {min_value}." + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "Valor da string é muito grande." + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "Um número válido é necessário." + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "Certifique-se de que não haja mais de {max_digits} dígitos no total." + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "Certifique-se de que não haja mais de {max_decimal_places} casas decimais." + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "Certifique-se de que não haja mais de {max_whole_digits} dígitos antes do ponto decimal." + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "Formato inválido para data e hora. Use um dos formatos a seguir: {format}." + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "Data e hora são necessários mas apenas data foi encontrada." + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "Formato inválido para data. Use um dos formatos a seguir: {format}." + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "Necessário uma data mas recebeu uma data e hora." + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "Tempo tem formato errado. Usa um desses em vez disso: {format}." + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "\"{input}\" não é um escolha válido." + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "Necessário uma lista de itens, mas recebeu tipo \"{input_type}\"." + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "Ficheiro não foi submetido." + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "Os dados submetidos nao foram um ficheiro. Certifique-se do tipo de codificação no formulário." + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "Nome do arquivo não pode ser determinado." + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "O arquivo submetido ésta vázio." + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "Certifique-se de que o nome do ficheiro tem menos de {max_length} caracteres (tem {length})." + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "Fazer upload de um imagem válido. O arquivo mandou não foi um imagem ou foi corrupto." + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "Página inválido \"{page_number}\": {message}." + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "Pk inválido \"{pk_value}\" - objeto não existe." + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "Tipo incorreto. Necessário valor pk, recebeu {data_type}." + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "Hyperlink inválido - URL não combinou." + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "Hyperlink inválido - URL combinou errado." + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "Hyperlink inválido - objeto não existe." + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "Tipo incorreto. Necessário string URL, recebeu {data_type}." + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "Objeto com {slug_name}={value} não existe." + +#: relations.py:296 +msgid "Invalid value." +msgstr "Valor inválido." + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "Data inválido. Necessário um dicionário mas recebeu {datatype}." + +#: validators.py:22 +msgid "This field must be unique." +msgstr "Esse campo deve ser unico." + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "Os campos {field_names} devem criar um set unico." + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "O campo deve ser unico pela data \"{date_field}\"." + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "O campo deve ser unico pelo anô \"{date_field}\"." + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "O campo deve ser unico pela mês \"{date_field}\"." + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "Versão inválido no cabeçalho \"Accept\"." + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "Versão inválido no caminho de URL." + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "Versão inválido no hostname." + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "Versão inválida no parâmetro de query." + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "Conta de usário desabilitada." + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "Impossível fazer login com as credenciais fornecidas." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "Obrigatório incluir \"usuário\" e \"senha\"." diff --git a/rest_framework/locale/ru/LC_MESSAGES/django.mo b/rest_framework/locale/ru/LC_MESSAGES/django.mo new file mode 100644 index 00000000..d1555f1f Binary files /dev/null and b/rest_framework/locale/ru/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/ru/LC_MESSAGES/django.po b/rest_framework/locale/ru/LC_MESSAGES/django.po new file mode 100644 index 00000000..38489747 --- /dev/null +++ b/rest_framework/locale/ru/LC_MESSAGES/django.po @@ -0,0 +1,325 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Mikhail Dmitriev , 2015 +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: Russian (http://www.transifex.com/projects/p/django-rest-framework/language/ru/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ru\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "Недопустимый заголовок. Не предоставлены учетные данные." + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "Недопустимый заголовок. Учетные данные не должны содержать пробелов." + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "Недопустимый заголовок. Учетные данные некорректно закодированны в base64." + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "Недопустимые имя пользователя или пароль." + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "Недопустимый заголовок токена. Не предоставлены учетные данные." + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "Недопустимый заголовок токена. Токен не должен содержать пробелов." + +#: authentication.py:168 +msgid "Invalid token." +msgstr "Недопустимый токен." + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "Пользователь неактивен или удален." + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "Произошла ошибка сервера." + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "Искаженный запрос." + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "Некорректные учетные данные." + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "Учетные данные не были предоставлены." + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "У вас нет прав для выполнения этой операции." + +#: exceptions.py:93 +msgid "Not found." +msgstr "Не найдено." + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "Метод \"{method}\" не разрешен." + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "Невозможно удовлетворить \"Accept\" заголовок запроса." + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "Неподдерживаемый тип данных \"{media_type}\" в запросе." + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "Запрос был проигнорирован." + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "Это поле обязательно." + +#: fields.py:154 +msgid "This field may not be null." +msgstr "Это поле не может быть null." + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "\"{input}\" не является корректным булевым значением." + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "Это поле не может быть пустым." + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "Убедитесь что в этом поле не больше {max_length} символов." + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "Убедитесь что в этом поле как минимум {min_length} символов." + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "Введите корректный адрес электронной почты." + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "Значение не соответствует требуемому паттерну." + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "Введите корректный \"slug\", состоящий из букв, цифр, знаков подчеркивания или дефисов." + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "Введите корректный URL." + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "Требуется целочисленное значение." + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "Убедитесь что значение меньше или равно {max_value}." + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "Убедитесь что значение больше или равно {min_value}." + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "Слишком длинное значение." + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "Требуется численное значение." + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "Убедитесь что в числе не больше {max_digits} знаков." + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "Убедитесь что в числе не больше {max_decimal_places} знаков в дробной части." + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "Убедитесь что в цисле не больше {max_whole_digits} знаков в целой части." + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "Неправильный формат datetime. Используйте один из этих форматов: {format}." + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "Ожидался datetime, но был получен date." + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "Неправильный формат date. Используйте один из этих форматов: {format}." + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "Ожидался date, но был получен datetime." + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "Неправильный формат времени. Используйте один из этих форматов: {format}." + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "\"{input}\" не является корректным значением." + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "Ожидался list со значениями, но был получен \"{input_type}\"." + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "Не был загружен файл." + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "Загруженный файл не является корректным файлом. " + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "Невозможно определить имя файла." + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "Загруженный файл пуст." + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "Убедитесь что имя файла меньше {max_length} символов (сейчас {length})." + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "Загрузите корректное изображение. Загруженный файл не является изображением, либо является испорченным." + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "Недопустимая страница \"{page_number}\": {message}." + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "Недопустимый первичный ключ \"{pk_value}\" - объект не существует." + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "Некорректный тип. Ожилалось значение первичного ключа, получен {data_type}." + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "Недопустимая ссылка - нет совпадения по URL." + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "Недопустимая ссылка - некорректное совпадение по URL," + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "Недопустимая ссылка - объект не существует." + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "Некорректный тип. Ожидался URL, получен {data_type}." + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "Объект с {slug_name}={value} не существует." + +#: relations.py:296 +msgid "Invalid value." +msgstr "Недопустимое значение." + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "Недопустимые данные. Ожидался dictionary, но был получен {datatype}." + +#: validators.py:22 +msgid "This field must be unique." +msgstr "" + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "" + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "" + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "" + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "" + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "" + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "" + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "" + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "" + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "Учетная запись пользователя отключена." + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "Невозможно войти с предоставленными учетными данными." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "Должен включать \"username\" и \"password\"." diff --git a/rest_framework/locale/sk/LC_MESSAGES/django.mo b/rest_framework/locale/sk/LC_MESSAGES/django.mo new file mode 100644 index 00000000..53bb95d8 Binary files /dev/null and b/rest_framework/locale/sk/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/sk/LC_MESSAGES/django.po b/rest_framework/locale/sk/LC_MESSAGES/django.po new file mode 100644 index 00000000..9dd378c0 --- /dev/null +++ b/rest_framework/locale/sk/LC_MESSAGES/django.po @@ -0,0 +1,325 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Stanislav Komanec , 2015 +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: Slovak (http://www.transifex.com/projects/p/django-rest-framework/language/sk/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: sk\n" +"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "Nesprávna hlavička. Neboli poskytnuté prihlasovacie údaje." + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "Nesprávna hlavička. Prihlasovacie údaje nesmú obsahovať medzery." + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "Nesprávna hlavička. Prihlasovacie údaje nie sú správne zakódované pomocou metódy base64." + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "Nesprávne prihlasovacie údaje." + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "Nesprávna token hlavička. Neboli poskytnuté prihlasovacie údaje." + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "Nesprávna token hlavička. Token hlavička nesmie obsahovať medzery." + +#: authentication.py:168 +msgid "Invalid token." +msgstr "Nesprávny token." + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "Daný používateľ je neaktívny, alebo zmazaný." + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "Vyskytla sa chyba na strane servera." + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "Požiadavok má nesprávny formát, alebo je poškodený." + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "Nesprávne prihlasovacie údaje." + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "Prihlasovacie údaje neboli zadané." + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "K danej akcii nemáte oprávnenie." + +#: exceptions.py:93 +msgid "Not found." +msgstr "Nebolo nájdené." + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "Metóda \"{method}\" nie je povolená." + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "Nie je možné vyhovieť požiadavku v hlavičke \"Accept\"." + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "Požiadavok obsahuje nepodporovaný media type: \"{media_type}\"." + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "" + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "" + +#: fields.py:154 +msgid "This field may not be null." +msgstr "" + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "" + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "" + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "" + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "" + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "" + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "" + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "" + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "" + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "" + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "" + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "" + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "" + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "" + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "" + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "" + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "" + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "" + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "" + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "" + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "" + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "" + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "" + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "" + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "" + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "" + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "" + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "" + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "" + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "" + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "" + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "" + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "" + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "" + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "" + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "" + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "" + +#: relations.py:296 +msgid "Invalid value." +msgstr "" + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "" + +#: validators.py:22 +msgid "This field must be unique." +msgstr "" + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "" + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "" + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "" + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "" + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "" + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "" + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "" + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "" + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "Daný používateľ je zablokovaný." + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "S danými prihlasovacími údajmi nebolo možné sa prihlásiť." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "Musí obsahovať parametre \"používateľské meno\" a \"heslo\"." diff --git a/rest_framework/locale/sv/LC_MESSAGES/django.mo b/rest_framework/locale/sv/LC_MESSAGES/django.mo new file mode 100644 index 00000000..a33b0cc5 Binary files /dev/null and b/rest_framework/locale/sv/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/sv/LC_MESSAGES/django.po b/rest_framework/locale/sv/LC_MESSAGES/django.po new file mode 100644 index 00000000..1602bf55 --- /dev/null +++ b/rest_framework/locale/sv/LC_MESSAGES/django.po @@ -0,0 +1,325 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Joakim Soderlund, 2015 +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: Swedish (http://www.transifex.com/projects/p/django-rest-framework/language/sv/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: sv\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "Ogiltig \"basic\"-header. Inga användaruppgifter tillhandahölls." + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "Ogiltig \"basic\"-header. Strängen för användaruppgifterna ska inte innehålla mellanslag." + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "Ogiltig \"basic\"-header. Användaruppgifterna är inte korrekt base64-kodade." + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "Ogiltigt användarnamn/lösenord." + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "Ogiltig \"token\"-header. Inga användaruppgifter tillhandahölls." + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "Ogiltig \"token\"-header. Strängen för referensen ska inte innehålla mellanslag." + +#: authentication.py:168 +msgid "Invalid token." +msgstr "Ogiltig \"token\"." + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "Användaren borttagen eller inaktiv." + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "Ett serverfel inträffade." + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "Ogiltig förfrågan." + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "Ogiltiga inloggningsuppgifter. " + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "Autentiseringsuppgifter ej tillhandahållna." + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "Du har inte tillåtelse att utföra denna förfrågan." + +#: exceptions.py:93 +msgid "Not found." +msgstr "Hittades inte." + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "Metoden \"{method}\" tillåts inte." + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "Kunde inte tillfredsställa förfrågans \"Accept\"-header." + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "Medietypen \"{media_type}\" stöds inte." + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "Förfrågan stoppades eftersom du har skickat för många." + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "Det här fältet är obligatoriskt." + +#: fields.py:154 +msgid "This field may not be null." +msgstr "Det här fältet får inte vara null." + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "\"{input}\" är inte ett giltigt booleskt värde." + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "Det här fältet får inte vara blankt." + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "Se till att detta fält inte har fler än {max_length} tecken." + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "Se till att detta fält har minst {min_length} tecken." + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "Ange en giltig mejladress." + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "Det här värdet matchar inte mallen." + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "Ange en giltig \"slug\" bestående av bokstäver, nummer, understreck eller bindestreck." + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "Ange en giltig URL." + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "Ett giltigt heltal krävs." + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "Se till att detta värde är mindre än eller lika med {max_value}." + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "Se till att detta värde är större än eller lika med {min_value}." + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "Textvärdet är för långt." + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "Ett giltigt nummer krävs." + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "Se till att det inte finns fler än totalt {max_digits} siffror." + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "Se till att det inte finns fler än {max_decimal_places} decimaler." + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "Se till att det inte finns fler än {max_whole_digits} siffror före decimalpunkten." + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "Datumtiden har fel format. Använd ett av dessa format istället: {format}." + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "Förväntade en datumtid men fick ett datum." + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "Datumet har fel format. Använde ett av dessa format istället: {format}." + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "Förväntade ett datum men fick en datumtid." + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "Tiden har fel format. Använd ett av dessa format istället: {format}." + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "\"{input}\" är inte ett giltigt val." + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "Förväntade en lista med element men fick typen \"{input_type}\"." + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "Ingen fil skickades." + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "Den skickade informationen var inte en fil. Kontrollera formulärets kodningstyp." + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "Inget filnamn kunde bestämmas." + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "Den skickade filen var tom." + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "Se till att det här filnamnet har högst {max_length} tecken (det har {length})." + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "Ladda upp en giltig bild. Filen du laddade upp var antingen inte en bild eller en skadad bild." + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "Ogiltigt sida \"{page_number}\": {message}." + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "Ogiltigt pk \"{pk_value}\" - Objektet finns inte." + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "Felaktig typ. Förväntade pk-värde, fick {data_type}." + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "Ogiltig hyperlänk - Ingen URL matchade." + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "Ogiltig hyperlänk - Felaktig URL-matching." + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "Ogiltig hyperlänk - Objektet finns inte." + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "Felaktig typ. Förväntade URL-sträng, fick {data_type}." + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "Objekt med {slug_name}={value} finns inte." + +#: relations.py:296 +msgid "Invalid value." +msgstr "Ogiltigt värde." + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "Ogiltig data. Förväntade en dictionary, men fick {datatype}." + +#: validators.py:22 +msgid "This field must be unique." +msgstr "Det här fältet måste vara unikt." + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "Fälten {field_names} måste skapa ett unikt set." + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "Det här fältet måste vara unikt för datumet \"{date_field}\"." + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "Det här fältet måste vara unikt för månaden \"{date_field}\"." + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "Det här fältet måste vara unikt för året \"{date_field}\"." + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "Ogiltig version i \"Accept\"-headern." + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "Ogiltig version i URL-resursen." + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "Ogiltig version i värdnamnet." + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "Ogiltig version i förfrågningsparametern." + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "Användarkontot är borttaget." + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "Kunde inte logga in med de angivna inloggningsuppgifterna." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "Användarnamn och lösenord måste anges." diff --git a/rest_framework/locale/tr/LC_MESSAGES/django.mo b/rest_framework/locale/tr/LC_MESSAGES/django.mo new file mode 100644 index 00000000..e6b848cf Binary files /dev/null and b/rest_framework/locale/tr/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/tr/LC_MESSAGES/django.po b/rest_framework/locale/tr/LC_MESSAGES/django.po new file mode 100644 index 00000000..5aabbeba --- /dev/null +++ b/rest_framework/locale/tr/LC_MESSAGES/django.po @@ -0,0 +1,328 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Ertaç Paprat , 2015 +# Mesut Can Gürle , 2015 +# Recep KIRMIZI , 2015 +# Ülgen Sarıkavak , 2015 +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: Turkish (http://www.transifex.com/projects/p/django-rest-framework/language/tr/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: tr\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "" + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "" + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "" + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "Geçersiz kullanıcı adı/parola" + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "Geçersiz token başlığı. Kimlik bilgileri eksik." + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "Geçersiz token başlığı. Token'da boşluk olmamalı." + +#: authentication.py:168 +msgid "Invalid token." +msgstr "Geçersiz token." + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "Kullanıcı aktif değil ya da silinmiş." + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "Sunucu hatası oluştu." + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "Bozuk istek." + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "Giriş bilgileri hatalı." + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "Giriş bilgileri verilmedi." + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "Bu işlemi yapmak için izniniz bulunmuyor." + +#: exceptions.py:93 +msgid "Not found." +msgstr "Bulunamadı." + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "\"{method}\" metoduna izin verilmiyor." + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "" + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "İstekte desteklenmeyen medya tipi: \"{media_type}\"." + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "" + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "Bu alan zorunlu." + +#: fields.py:154 +msgid "This field may not be null." +msgstr "Bu alan boş bırakılmamalı." + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "\"{input}\" geçerli bir boolean değil." + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "Bu alan boş bırakılmamalı." + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "Bu alanın {max_length} karakterden fazla karakter barındırmadığından emin olun." + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "Bu alanın en az {min_length} karakter barındırdığından emin olun." + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "Geçerli bir e-posta adresi girin." + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "Bu değer gereken düzenli ifade deseni ile uyuşmuyor." + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "Harf, rakam, altçizgi veya tireden oluşan geçerli bir \"slug\" giriniz." + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "Geçerli bir URL girin." + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "Geçerli bir tam sayı girin." + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "Değerin {max_value} değerinden küçük ya da eşit olduğundan emin olun." + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "Değerin {min_value} değerinden büyük ya da eşit olduğundan emin olun." + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "String değeri çok uzun." + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "Geçerli bir numara gerekiyor." + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "Toplamda {max_digits} haneden fazla hane olmadığından emin olun." + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "Ondalık basamak değerinin {max_decimal_places} haneden fazla olmadığından emin olun." + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "Ondalık ayracından önce {max_whole_digits} basamaktan fazla olmadığından emin olun." + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "Datetime alanı yanlış biçimde. {format} biçimlerinden birini kullanın." + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "Datetime değeri bekleniyor, ama date değeri geldi." + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "Tarih biçimi yanlış. {format} biçimlerinden birini kullanın." + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "Date tipi beklenmekteydi, fakat datetime tipi geldi." + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "Time biçimi yanlış. {format} biçimlerinden birini kullanın." + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "\"{input}\" geçerli bir seçim değil." + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "Elemanların listesi beklenirken \"{input_type}\" alındı." + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "Hiçbir dosya verilmedi." + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "Gönderilen veri dosya değil. Formdaki kodlama tipini kontrol edin." + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "Hiçbir dosya adı belirlenemedi." + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "Gönderilen dosya boş." + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "Bu dosya adının en fazla {max_length} karakter uzunluğunda olduğundan emin olun. (şu anda {length} karakter)." + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "Geçerli bir resim yükleyin. Yüklediğiniz dosya resim değil ya da bozuk." + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "Geçersiz sayfa \"{page_number}\":{message}." + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "Geçersiz pk \"{pk_value}\" - obje bulunamadı." + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "Hatalı tip. Pk değeri beklenirken, alınan {data_type}." + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "Geçersiz bağlantı - Hiçbir URL eşleşmedi." + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "Geçersiz bağlantı - Yanlış URL eşleşmesi." + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "Geçersiz bağlantı - Obje bulunamadı." + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "Hatalı tip. URL metni bekleniyor, {data_type} alındı." + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "{slug_name}={value} değerini taşıyan obje bulunamadı." + +#: relations.py:296 +msgid "Invalid value." +msgstr "Geçersiz değer." + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "Geçersiz veri. Sözlük bekleniyordu fakat {datatype} geldi. " + +#: validators.py:22 +msgid "This field must be unique." +msgstr "Bu alan eşsiz olmalı." + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "{field_names} hep birlikte eşsiz bir küme oluşturmalılar." + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "Bu alan \"{date_field}\" tarihine göre eşsiz olmalı." + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "Bu alan \"{date_field}\" ayına göre eşsiz olmalı." + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "Bu alan \"{date_field}\" yılına göre eşsiz olmalı." + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "\"Accept\" başlığındaki sürüm geçersiz." + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "URL dizininde geçersiz versiyon." + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "Host adında geçersiz versiyon." + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "Sorgu parametresinde geçersiz versiyon." + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "Kullanıcı hesabı devre dışı bırakılmış." + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "Verilen bilgiler ile giriş sağlanamadı." + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "\"Kullanıcı Adı\" ve \"Parola\" eklenmeli." diff --git a/rest_framework/locale/uk/LC_MESSAGES/django.mo b/rest_framework/locale/uk/LC_MESSAGES/django.mo new file mode 100644 index 00000000..fc335054 Binary files /dev/null and b/rest_framework/locale/uk/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/uk/LC_MESSAGES/django.po b/rest_framework/locale/uk/LC_MESSAGES/django.po new file mode 100644 index 00000000..93fc2bf9 --- /dev/null +++ b/rest_framework/locale/uk/LC_MESSAGES/django.po @@ -0,0 +1,324 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: Ukrainian (http://www.transifex.com/projects/p/django-rest-framework/language/uk/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: uk\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "" + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "" + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "" + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "" + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "" + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "" + +#: authentication.py:168 +msgid "Invalid token." +msgstr "" + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "" + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "" + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "" + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "" + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "" + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "" + +#: exceptions.py:93 +msgid "Not found." +msgstr "" + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "" + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "" + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "" + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "" + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "" + +#: fields.py:154 +msgid "This field may not be null." +msgstr "" + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "" + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "" + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "" + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "" + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "" + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "" + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "" + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "" + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "" + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "" + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "" + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "" + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "" + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "" + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "" + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "" + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "" + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "" + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "" + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "" + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "" + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "" + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "" + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "" + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "" + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "" + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "" + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "" + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "" + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "" + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "" + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "" + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "" + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "" + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "" + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "" + +#: relations.py:296 +msgid "Invalid value." +msgstr "" + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "" + +#: validators.py:22 +msgid "This field must be unique." +msgstr "" + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "" + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "" + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "" + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "" + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "" + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "" + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "" + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "" + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "" + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "" + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "" diff --git a/rest_framework/locale/zh_CN/LC_MESSAGES/django.mo b/rest_framework/locale/zh_CN/LC_MESSAGES/django.mo new file mode 100644 index 00000000..6e7073bd Binary files /dev/null and b/rest_framework/locale/zh_CN/LC_MESSAGES/django.mo differ diff --git a/rest_framework/locale/zh_CN/LC_MESSAGES/django.po b/rest_framework/locale/zh_CN/LC_MESSAGES/django.po new file mode 100644 index 00000000..01128859 --- /dev/null +++ b/rest_framework/locale/zh_CN/LC_MESSAGES/django.po @@ -0,0 +1,325 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Lele Long , 2015 +msgid "" +msgstr "" +"Project-Id-Version: Django REST framework\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-01-30 16:23+0000\n" +"PO-Revision-Date: 2015-01-30 16:27+0000\n" +"Last-Translator: Thomas Christie \n" +"Language-Team: Chinese (China) (http://www.transifex.com/projects/p/django-rest-framework/language/zh_CN/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: zh_CN\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: authentication.py:69 +msgid "Invalid basic header. No credentials provided." +msgstr "没有提供认证信息(基本认证HTTP头无效)。" + +#: authentication.py:72 +msgid "Invalid basic header. Credentials string should not contain spaces." +msgstr "认证字符串不应该包含空格(基本认证HTTP头无效)。" + +#: authentication.py:78 +msgid "Invalid basic header. Credentials not correctly base64 encoded." +msgstr "认证字符串base64编码错误(基本认证HTTP头无效)。" + +#: authentication.py:90 +msgid "Invalid username/password." +msgstr "用户名或者密码错误。" + +#: authentication.py:156 +msgid "Invalid token header. No credentials provided." +msgstr "没有提供认证信息(认证令牌HTTP头无效)。" + +#: authentication.py:159 +msgid "Invalid token header. Token string should not contain spaces." +msgstr "认证令牌字符串不应该包含空格(无效的认证令牌HTTP头)。" + +#: authentication.py:168 +msgid "Invalid token." +msgstr "认证令牌无效。" + +#: authentication.py:171 +msgid "User inactive or deleted." +msgstr "用户未激活或者已删除。" + +#: exceptions.py:38 +msgid "A server error occurred." +msgstr "服务器出现了错误。" + +#: exceptions.py:73 +msgid "Malformed request." +msgstr "畸形的请求。" + +#: exceptions.py:78 +msgid "Incorrect authentication credentials." +msgstr "不正确的身份认证凭据。" + +#: exceptions.py:83 +msgid "Authentication credentials were not provided." +msgstr "身份认证凭据未提供。" + +#: exceptions.py:88 +msgid "You do not have permission to perform this action." +msgstr "您没有执行该操作的权限。" + +#: exceptions.py:93 +msgid "Not found." +msgstr "未找到。" + +#: exceptions.py:98 +msgid "Method \"{method}\" not allowed." +msgstr "方法 “{method}” 不被允许。" + +#: exceptions.py:109 +msgid "Could not satisfy the request Accept header." +msgstr "无法满足Accept HTTP头的请求。" + +#: exceptions.py:121 +msgid "Unsupported media type \"{media_type}\" in request." +msgstr "不支持请求中的媒体类型 “{media_type}”。" + +#: exceptions.py:134 +msgid "Request was throttled." +msgstr "请求被限速。" + +#: fields.py:153 relations.py:132 relations.py:156 validators.py:77 +#: validators.py:155 +msgid "This field is required." +msgstr "这个字段是必填项。" + +#: fields.py:154 +msgid "This field may not be null." +msgstr "这个值不能为 null。" + +#: fields.py:487 fields.py:515 +msgid "\"{input}\" is not a valid boolean." +msgstr "“{input}” 不是合法的布尔值。" + +#: fields.py:550 +msgid "This field may not be blank." +msgstr "此字段不能为空。" + +#: fields.py:551 fields.py:1324 +msgid "Ensure this field has no more than {max_length} characters." +msgstr "请确保这个字段不能超过 {max_length} 个字符。" + +#: fields.py:552 +msgid "Ensure this field has at least {min_length} characters." +msgstr "请确保这个字段至少包含 {min_length} 个字符。" + +#: fields.py:587 +msgid "Enter a valid email address." +msgstr "请输入合法的邮件地址。" + +#: fields.py:604 +msgid "This value does not match the required pattern." +msgstr "输入值不匹配要求的模式。" + +#: fields.py:615 +msgid "" +"Enter a valid \"slug\" consisting of letters, numbers, underscores or " +"hyphens." +msgstr "请输入合法的“短语“,只能包含字母,数字,下划线或者中划线。" + +#: fields.py:627 +msgid "Enter a valid URL." +msgstr "请输入合法的URL。" + +#: fields.py:638 +msgid "\"{value}\" is not a valid UUID." +msgstr "" + +#: fields.py:657 +msgid "A valid integer is required." +msgstr "请填写合法的整数值。" + +#: fields.py:658 fields.py:692 fields.py:725 +msgid "Ensure this value is less than or equal to {max_value}." +msgstr "请确保该值小于或者等于 {max_value}。" + +#: fields.py:659 fields.py:693 fields.py:726 +msgid "Ensure this value is greater than or equal to {min_value}." +msgstr "请确保该值大于或者等于 {min_value}。" + +#: fields.py:660 fields.py:694 fields.py:730 +msgid "String value too large." +msgstr "字符值太长。" + +#: fields.py:691 fields.py:724 +msgid "A valid number is required." +msgstr "请填写合法的数字。" + +#: fields.py:727 +msgid "Ensure that there are no more than {max_digits} digits in total." +msgstr "请确保总计不超过 {max_digits} 个数字。" + +#: fields.py:728 +msgid "" +"Ensure that there are no more than {max_decimal_places} decimal places." +msgstr "请确保总计不超过 {max_decimal_places} 个小数位。" + +#: fields.py:729 +msgid "" +"Ensure that there are no more than {max_whole_digits} digits before the " +"decimal point." +msgstr "请确保小数点前不超过 {max_whole_digits} 个数字。" + +#: fields.py:813 +msgid "Datetime has wrong format. Use one of these formats instead: {format}." +msgstr "日期时间格式错误。请从这些格式中选择:{format}。" + +#: fields.py:814 +msgid "Expected a datetime but got a date." +msgstr "期望为日期时间,得到的是日期。" + +#: fields.py:878 +msgid "Date has wrong format. Use one of these formats instead: {format}." +msgstr "日期格式错误。请从这些格式中选择:{format}。" + +#: fields.py:879 +msgid "Expected a date but got a datetime." +msgstr "期望为日期,得到的是日期时间。" + +#: fields.py:936 +msgid "Time has wrong format. Use one of these formats instead: {format}." +msgstr "时间格式错误。请从这些格式中选择:{format}。" + +#: fields.py:992 fields.py:1036 +msgid "\"{input}\" is not a valid choice." +msgstr "“{input}” 不是合法选项。" + +#: fields.py:1037 fields.py:1151 serializers.py:482 +msgid "Expected a list of items but got type \"{input_type}\"." +msgstr "期望为一个包含物件的列表,得到的类型是“{input_type}”。" + +#: fields.py:1067 +msgid "No file was submitted." +msgstr "没有提交任何文件。" + +#: fields.py:1068 +msgid "" +"The submitted data was not a file. Check the encoding type on the form." +msgstr "提交的数据不是一个文件。请检查表单的编码类型。" + +#: fields.py:1069 +msgid "No filename could be determined." +msgstr "无法检测到文件名。" + +#: fields.py:1070 +msgid "The submitted file is empty." +msgstr "提交的是空文件。" + +#: fields.py:1071 +msgid "" +"Ensure this filename has at most {max_length} characters (it has {length})." +msgstr "确保该文件名最多包含 {max_length} 个字符 ( 当前长度为{length} ) 。" + +#: fields.py:1113 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "请上传有效图片。您上传的该文件不是图片或者图片已经损坏。" + +#: fields.py:1188 +msgid "Expected a dictionary of items but got type \"{input_type}\"." +msgstr "" + +#: pagination.py:221 +msgid "Invalid page \"{page_number}\": {message}." +msgstr "无效页面 “{page_number}”:{message}。" + +#: pagination.py:442 +msgid "Invalid cursor" +msgstr "" + +#: relations.py:133 +msgid "Invalid pk \"{pk_value}\" - object does not exist." +msgstr "无效主键 “{pk_value}” - 对象不存在。" + +#: relations.py:134 +msgid "Incorrect type. Expected pk value, received {data_type}." +msgstr "类型错误。期望为主键,得到的类型为 {data_type}。" + +#: relations.py:157 +msgid "Invalid hyperlink - No URL match." +msgstr "无效超链接 -没有匹配的URL。" + +#: relations.py:158 +msgid "Invalid hyperlink - Incorrect URL match." +msgstr "无效超链接 -错误的URL匹配。" + +#: relations.py:159 +msgid "Invalid hyperlink - Object does not exist." +msgstr "无效超链接 -对象不存在。" + +#: relations.py:160 +msgid "Incorrect type. Expected URL string, received {data_type}." +msgstr "类型错误。期望为URL字符串,得到的类型是 {data_type}。" + +#: relations.py:295 +msgid "Object with {slug_name}={value} does not exist." +msgstr "属性 {slug_name} 为 {value} 的对象不存在。" + +#: relations.py:296 +msgid "Invalid value." +msgstr "无效值。" + +#: serializers.py:299 +msgid "Invalid data. Expected a dictionary, but got {datatype}." +msgstr "无效数据。期待为字典类型,得到的是 {datatype} 。" + +#: validators.py:22 +msgid "This field must be unique." +msgstr "该字段必须唯一。" + +#: validators.py:76 +msgid "The fields {field_names} must make a unique set." +msgstr "字段 {field_names} 必须能构成唯一集合。" + +#: validators.py:219 +msgid "This field must be unique for the \"{date_field}\" date." +msgstr "该字段必须在日期 “{date_field}” 唯一。" + +#: validators.py:234 +msgid "This field must be unique for the \"{date_field}\" month." +msgstr "该字段必须在月份 “{date_field}” 唯一。" + +#: validators.py:247 +msgid "This field must be unique for the \"{date_field}\" year." +msgstr "该字段必须在年 “{date_field}” 唯一。" + +#: versioning.py:39 +msgid "Invalid version in \"Accept\" header." +msgstr "“Accept” HTTP头包含无效版本。" + +#: versioning.py:70 versioning.py:112 +msgid "Invalid version in URL path." +msgstr "URl路径包含无效版本。" + +#: versioning.py:138 +msgid "Invalid version in hostname." +msgstr "主机名包含无效版本。" + +#: versioning.py:160 +msgid "Invalid version in query parameter." +msgstr "请求参数里包含无效版本。" + +#: authtoken/serializers.py:20 +msgid "User account is disabled." +msgstr "用户账户已禁用。" + +#: authtoken/serializers.py:23 +msgid "Unable to log in with provided credentials." +msgstr "无法使用提供的认证信息登录。" + +#: authtoken/serializers.py:26 +msgid "Must include \"username\" and \"password\"." +msgstr "必须包含 “用户名” 和 “密码”。" diff --git a/rest_framework/views.py b/rest_framework/views.py index 12bb78bd..995ddd0f 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -2,12 +2,10 @@ Provides an APIView class that is the base of all views in REST framework. """ from __future__ import unicode_literals -import inspect -import warnings - from django.core.exceptions import PermissionDenied from django.http import Http404 from django.utils.encoding import smart_text +from django.utils.translation import ugettext_lazy as _ from django.views.decorators.csrf import csrf_exempt from rest_framework import status, exceptions from rest_framework.compat import HttpResponseBase, View @@ -15,6 +13,8 @@ from rest_framework.request import Request from rest_framework.response import Response from rest_framework.settings import api_settings from rest_framework.utils import formatting +import inspect +import warnings def get_view_name(view_cls, suffix=None): @@ -74,11 +74,11 @@ def exception_handler(exc, context): return Response(data, status=exc.status_code, headers=headers) elif isinstance(exc, Http404): - data = {'detail': 'Not found'} + data = {'detail': _('Not found.')} return Response(data, status=status.HTTP_404_NOT_FOUND) elif isinstance(exc, PermissionDenied): - data = {'detail': 'Permission denied'} + data = {'detail': _('Permission denied.')} return Response(data, status=status.HTTP_403_FORBIDDEN) # Note: Unhandled exceptions will raise a 500 error. -- cgit v1.2.3 From 2cc4cb24652366c6622af08370a0c04b429aa4b8 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sat, 31 Jan 2015 08:53:40 +0000 Subject: Fix error text in test. --- rest_framework/views.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/views.py b/rest_framework/views.py index 995ddd0f..9445c840 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -4,6 +4,7 @@ Provides an APIView class that is the base of all views in REST framework. from __future__ import unicode_literals from django.core.exceptions import PermissionDenied from django.http import Http404 +from django.utils import six from django.utils.encoding import smart_text from django.utils.translation import ugettext_lazy as _ from django.views.decorators.csrf import csrf_exempt @@ -74,11 +75,13 @@ def exception_handler(exc, context): return Response(data, status=exc.status_code, headers=headers) elif isinstance(exc, Http404): - data = {'detail': _('Not found.')} + msg = _('Not found.') + data = {'detail': six.text_type(msg)} return Response(data, status=status.HTTP_404_NOT_FOUND) elif isinstance(exc, PermissionDenied): - data = {'detail': _('Permission denied.')} + msg = _('Permission denied.') + data = {'detail': six.text_type(msg)} return Response(data, status=status.HTTP_403_FORBIDDEN) # Note: Unhandled exceptions will raise a 500 error. -- cgit v1.2.3 From e63f49bd1d55501f766ca2e3f9c0c9fa3cfa19ab Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Sat, 31 Jan 2015 19:59:52 +0000 Subject: Fix field mappings for 1.8 fields --- rest_framework/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index a3b8196b..a91fe23e 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -1330,13 +1330,13 @@ class ModelSerializer(Serializer): if hasattr(models, 'UUIDField'): - ModelSerializer._field_mapping[models.UUIDField] = UUIDField + ModelSerializer.serializer_field_mapping[models.UUIDField] = UUIDField if postgres_fields: class CharMappingField(DictField): child = CharField() - ModelSerializer._field_mapping[postgres_fields.HStoreField] = CharMappingField + ModelSerializer.serializer_field_mapping[postgres_fields.HStoreField] = CharMappingField class HyperlinkedModelSerializer(ModelSerializer): -- cgit v1.2.3 From 37dce89354ab2c94fefeb0a20b6265fef98caddc Mon Sep 17 00:00:00 2001 From: José Padilla Date: Sun, 1 Feb 2015 15:33:34 -0400 Subject: Add support for Django 1.8’s ArrayField --- rest_framework/serializers.py | 1 + 1 file changed, 1 insertion(+) (limited to 'rest_framework') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index a91fe23e..520b9774 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -1337,6 +1337,7 @@ if postgres_fields: child = CharField() ModelSerializer.serializer_field_mapping[postgres_fields.HStoreField] = CharMappingField + ModelSerializer.serializer_field_mapping[postgres_fields.ArrayField] = ListField class HyperlinkedModelSerializer(ModelSerializer): -- cgit v1.2.3 From 77d061d234e03004f34058028707ecddfc730fae Mon Sep 17 00:00:00 2001 From: Brandon Cazander Date: Wed, 28 Jan 2015 17:08:34 -0800 Subject: Provide rest_framework.resolve. Fixes #2489 --- rest_framework/relations.py | 7 ++++--- rest_framework/reverse.py | 15 ++++++++++++++- rest_framework/versioning.py | 16 ++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 66857a41..809d3db9 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -1,7 +1,7 @@ # coding: utf-8 from __future__ import unicode_literals from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured -from django.core.urlresolvers import resolve, get_script_prefix, NoReverseMatch, Resolver404 +from django.core.urlresolvers import get_script_prefix, NoReverseMatch, Resolver404 from django.db.models.query import QuerySet from django.utils import six from django.utils.encoding import smart_text @@ -9,7 +9,7 @@ from django.utils.six.moves.urllib import parse as urlparse from django.utils.translation import ugettext_lazy as _ from rest_framework.compat import OrderedDict from rest_framework.fields import get_attribute, empty, Field -from rest_framework.reverse import reverse +from rest_framework.reverse import reverse, resolve from rest_framework.utils import html @@ -205,6 +205,7 @@ class HyperlinkedRelatedField(RelatedField): return self.reverse(view_name, kwargs=kwargs, request=request, format=format) def to_internal_value(self, data): + request = self.context.get('request', None) try: http_prefix = data.startswith(('http:', 'https:')) except AttributeError: @@ -218,7 +219,7 @@ class HyperlinkedRelatedField(RelatedField): data = '/' + data[len(prefix):] try: - match = self.resolve(data) + match = self.resolve(data, request=request) except Resolver404: self.fail('no_match') diff --git a/rest_framework/reverse.py b/rest_framework/reverse.py index 8fcca55b..0d1d94a7 100644 --- a/rest_framework/reverse.py +++ b/rest_framework/reverse.py @@ -1,12 +1,25 @@ """ -Provide reverse functions that return fully qualified URLs +Provide urlresolver functions that return fully qualified URLs or view names """ from __future__ import unicode_literals from django.core.urlresolvers import reverse as django_reverse +from django.core.urlresolvers import resolve as django_resolve from django.utils import six from django.utils.functional import lazy +def resolve(path, urlconf=None, request=None): + """ + If versioning is being used then we pass any `resolve` calls through + to the versioning scheme instance, so that the resulting view name + can be modified if needed. + """ + scheme = getattr(request, 'versioning_scheme', None) + if scheme is not None: + return scheme.resolve(path, urlconf, request) + return django_resolve(path, urlconf) + + def reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra): """ If versioning is being used then we pass any `reverse` calls through diff --git a/rest_framework/versioning.py b/rest_framework/versioning.py index a07b629f..a76da17a 100644 --- a/rest_framework/versioning.py +++ b/rest_framework/versioning.py @@ -1,6 +1,8 @@ # coding: utf-8 from __future__ import unicode_literals from django.utils.translation import ugettext_lazy as _ +from django.core.urlresolvers import resolve as django_resolve +from django.core.urlresolvers import ResolverMatch from rest_framework import exceptions from rest_framework.compat import unicode_http_header from rest_framework.reverse import _reverse @@ -24,6 +26,9 @@ class BaseVersioning(object): def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): return _reverse(viewname, args, kwargs, request, format, **extra) + def resolve(self, path, urlconf=None): + return django_resolve(path, urlconf) + def is_allowed_version(self, version): if not self.allowed_versions: return True @@ -127,6 +132,17 @@ class NamespaceVersioning(BaseVersioning): viewname, args, kwargs, request, format, **extra ) + def resolve(self, path, urlconf=None, request=None): + match = django_resolve(path, urlconf) + if match.namespace: + _, view_name = match.view_name.split(':') + return ResolverMatch(func=match.func, + args=match.args, + kwargs=match.kwargs, + url_name=view_name, + app_name=match.app_name) + return match + class HostNameVersioning(BaseVersioning): """ -- cgit v1.2.3 From 8b4ce5c636a9abb33029e48f969bbdf38f97ca1f Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Wed, 4 Feb 2015 09:07:10 +0000 Subject: Minor authentication message improvement. --- rest_framework/authentication.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index 11db0585..a75cd30c 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -86,8 +86,13 @@ class BasicAuthentication(BaseAuthentication): Authenticate the userid and password against username and password. """ user = authenticate(username=userid, password=password) - if user is None or not user.is_active: + + if user is None: raise exceptions.AuthenticationFailed(_('Invalid username/password.')) + + if not user.is_active: + raise exceptions.AuthenticationFailed(_('User inactive or deleted.')) + return (user, None) def authenticate_header(self, request): -- cgit v1.2.3 From e1c45133126e0c47b8470b4cf7a43c6a7f4fca43 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Thu, 5 Feb 2015 00:58:09 +0000 Subject: Fix NamespaceVersioning with hyperlinked serializer fields --- rest_framework/relations.py | 20 +++++++++++++------- rest_framework/reverse.py | 13 ------------- rest_framework/versioning.py | 19 +++---------------- 3 files changed, 16 insertions(+), 36 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 809d3db9..0b7c9d86 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -1,7 +1,7 @@ # coding: utf-8 from __future__ import unicode_literals from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured -from django.core.urlresolvers import get_script_prefix, NoReverseMatch, Resolver404 +from django.core.urlresolvers import get_script_prefix, resolve, NoReverseMatch, Resolver404 from django.db.models.query import QuerySet from django.utils import six from django.utils.encoding import smart_text @@ -9,7 +9,7 @@ from django.utils.six.moves.urllib import parse as urlparse from django.utils.translation import ugettext_lazy as _ from rest_framework.compat import OrderedDict from rest_framework.fields import get_attribute, empty, Field -from rest_framework.reverse import reverse, resolve +from rest_framework.reverse import reverse from rest_framework.utils import html @@ -167,11 +167,10 @@ class HyperlinkedRelatedField(RelatedField): self.lookup_url_kwarg = kwargs.pop('lookup_url_kwarg', self.lookup_field) self.format = kwargs.pop('format', None) - # We include these simply for dependency injection in tests. - # We can't add them as class attributes or they would expect an + # We include this simply for dependency injection in tests. + # We can't add it as a class attributes or it would expect an # implicit `self` argument to be passed. self.reverse = reverse - self.resolve = resolve super(HyperlinkedRelatedField, self).__init__(**kwargs) @@ -219,11 +218,18 @@ class HyperlinkedRelatedField(RelatedField): data = '/' + data[len(prefix):] try: - match = self.resolve(data, request=request) + match = resolve(data) except Resolver404: self.fail('no_match') - if match.view_name != self.view_name: + try: + expected_viewname = request.versioning_scheme.get_versioned_viewname( + self.view_name, request + ) + except AttributeError: + expected_viewname = self.view_name + + if match.view_name != expected_viewname: self.fail('incorrect_match') try: diff --git a/rest_framework/reverse.py b/rest_framework/reverse.py index 0d1d94a7..a251d99d 100644 --- a/rest_framework/reverse.py +++ b/rest_framework/reverse.py @@ -3,23 +3,10 @@ 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 resolve as django_resolve from django.utils import six from django.utils.functional import lazy -def resolve(path, urlconf=None, request=None): - """ - If versioning is being used then we pass any `resolve` calls through - to the versioning scheme instance, so that the resulting view name - can be modified if needed. - """ - scheme = getattr(request, 'versioning_scheme', None) - if scheme is not None: - return scheme.resolve(path, urlconf, request) - return django_resolve(path, urlconf) - - def reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra): """ If versioning is being used then we pass any `reverse` calls through diff --git a/rest_framework/versioning.py b/rest_framework/versioning.py index a76da17a..51b886f3 100644 --- a/rest_framework/versioning.py +++ b/rest_framework/versioning.py @@ -1,8 +1,6 @@ # coding: utf-8 from __future__ import unicode_literals from django.utils.translation import ugettext_lazy as _ -from django.core.urlresolvers import resolve as django_resolve -from django.core.urlresolvers import ResolverMatch from rest_framework import exceptions from rest_framework.compat import unicode_http_header from rest_framework.reverse import _reverse @@ -26,9 +24,6 @@ class BaseVersioning(object): def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): return _reverse(viewname, args, kwargs, request, format, **extra) - def resolve(self, path, urlconf=None): - return django_resolve(path, urlconf) - def is_allowed_version(self, version): if not self.allowed_versions: return True @@ -127,21 +122,13 @@ class NamespaceVersioning(BaseVersioning): def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): if request.version is not None: - viewname = request.version + ':' + viewname + viewname = self.get_versioned_viewname(viewname, request) return super(NamespaceVersioning, self).reverse( viewname, args, kwargs, request, format, **extra ) - def resolve(self, path, urlconf=None, request=None): - match = django_resolve(path, urlconf) - if match.namespace: - _, view_name = match.view_name.split(':') - return ResolverMatch(func=match.func, - args=match.args, - kwargs=match.kwargs, - url_name=view_name, - app_name=match.app_name) - return match + def get_versioned_viewname(self, viewname, request): + return request.version + ':' + viewname class HostNameVersioning(BaseVersioning): -- cgit v1.2.3 From 48fa77c09e2198c7877a724a46230caedcc7b529 Mon Sep 17 00:00:00 2001 From: José Padilla Date: Wed, 4 Feb 2015 23:33:59 -0400 Subject: Add child to ListField when using ArrayField --- rest_framework/serializers.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'rest_framework') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 520b9774..84e4961b 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -986,15 +986,25 @@ class ModelSerializer(Serializer): # Fields with choices get coerced into `ChoiceField` # instead of using their regular typed field. field_class = ChoiceField + if not issubclass(field_class, ModelField): # `model_field` is only valid for the fallback case of # `ModelField`, which is used when no other typed field # matched to the model field. field_kwargs.pop('model_field', None) + if not issubclass(field_class, CharField) and not issubclass(field_class, ChoiceField): # `allow_blank` is only valid for textual fields. field_kwargs.pop('allow_blank', None) + if postgres_fields and isinstance(model_field, postgres_fields.ArrayField): + child_model_field = model_field.base_field.base_field + child_field_class, child_field_kwargs = self.build_standard_field( + 'child', child_model_field + ) + + field_kwargs['child'] = child_field_class(**child_field_kwargs) + return field_class, field_kwargs def build_relational_field(self, field_name, relation_info): -- cgit v1.2.3 From c696b0ba0ced9527c8f4ad1bf6f71546d8fa65c2 Mon Sep 17 00:00:00 2001 From: José Padilla Date: Thu, 5 Feb 2015 10:12:14 -0400 Subject: Fix possible nested array fields --- rest_framework/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rest_framework') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 84e4961b..18821958 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -998,7 +998,7 @@ class ModelSerializer(Serializer): field_kwargs.pop('allow_blank', None) if postgres_fields and isinstance(model_field, postgres_fields.ArrayField): - child_model_field = model_field.base_field.base_field + child_model_field = model_field.base_field child_field_class, child_field_kwargs = self.build_standard_field( 'child', child_model_field ) -- cgit v1.2.3 From 1f996128458570a909d13f15c3d739fb12111984 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 6 Feb 2015 13:21:35 +0000 Subject: Upgrade pending deprecations to deprecations --- rest_framework/request.py | 12 ++++++------ rest_framework/serializers.py | 12 ++++++------ rest_framework/views.py | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) (limited to 'rest_framework') diff --git a/rest_framework/request.py b/rest_framework/request.py index bf6ff670..86fb1ef1 100644 --- a/rest_framework/request.py +++ b/rest_framework/request.py @@ -219,8 +219,8 @@ class Request(object): Synonym for `.query_params`, for backwards compatibility. """ warnings.warn( - "`request.QUERY_PARAMS` is pending deprecation. Use `request.query_params` instead.", - PendingDeprecationWarning, + "`request.QUERY_PARAMS` is deprecated. Use `request.query_params` instead.", + DeprecationWarning, stacklevel=1 ) return self._request.GET @@ -240,8 +240,8 @@ class Request(object): arbitrary parsers, and also works on methods other than POST (eg PUT). """ warnings.warn( - "`request.DATA` is pending deprecation. Use `request.data` instead.", - PendingDeprecationWarning, + "`request.DATA` is deprecated. Use `request.data` instead.", + DeprecationWarning, stacklevel=1 ) if not _hasattr(self, '_data'): @@ -257,8 +257,8 @@ class Request(object): arbitrary parsers, and also works on methods other than POST (eg PUT). """ warnings.warn( - "`request.FILES` is pending deprecation. Use `request.data` instead.", - PendingDeprecationWarning, + "`request.FILES` is deprecated. Use `request.data` instead.", + DeprecationWarning, stacklevel=1 ) if not _hasattr(self, '_files'): diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 18821958..18b810df 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -1103,9 +1103,9 @@ class ModelSerializer(Serializer): write_only_fields = getattr(self.Meta, 'write_only_fields', None) if write_only_fields is not None: warnings.warn( - "The `Meta.write_only_fields` option is pending deprecation. " + "The `Meta.write_only_fields` option is deprecated. " "Use `Meta.extra_kwargs={: {'write_only': True}}` instead.", - PendingDeprecationWarning, + DeprecationWarning, stacklevel=3 ) for field_name in write_only_fields: @@ -1116,9 +1116,9 @@ class ModelSerializer(Serializer): view_name = getattr(self.Meta, 'view_name', None) if view_name is not None: warnings.warn( - "The `Meta.view_name` option is pending deprecation. " + "The `Meta.view_name` option is deprecated. " "Use `Meta.extra_kwargs={'url': {'view_name': ...}}` instead.", - PendingDeprecationWarning, + DeprecationWarning, stacklevel=3 ) kwargs = extra_kwargs.get(api_settings.URL_FIELD_NAME, {}) @@ -1128,9 +1128,9 @@ class ModelSerializer(Serializer): lookup_field = getattr(self.Meta, 'lookup_field', None) if lookup_field is not None: warnings.warn( - "The `Meta.lookup_field` option is pending deprecation. " + "The `Meta.lookup_field` option is deprecated. " "Use `Meta.extra_kwargs={'url': {'lookup_field': ...}}` instead.", - PendingDeprecationWarning, + DeprecationWarning, stacklevel=3 ) kwargs = extra_kwargs.get(api_settings.URL_FIELD_NAME, {}) diff --git a/rest_framework/views.py b/rest_framework/views.py index 9445c840..b4abc4d9 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -409,7 +409,7 @@ class APIView(View): warnings.warn( 'The `exception_handler(exc)` call signature is deprecated. ' 'Use `exception_handler(exc, context) instead.', - PendingDeprecationWarning + DeprecationWarning ) response = exception_handler(exc) else: -- cgit v1.2.3