diff options
Diffstat (limited to 'rest_framework')
| -rw-r--r-- | rest_framework/__init__.py | 2 | ||||
| -rw-r--r-- | rest_framework/authentication.py | 2 | ||||
| -rw-r--r-- | rest_framework/fields.py | 8 | ||||
| -rw-r--r-- | rest_framework/filters.py | 1 | ||||
| -rw-r--r-- | rest_framework/pagination.py | 9 | ||||
| -rw-r--r-- | rest_framework/parsers.py | 20 | ||||
| -rw-r--r-- | rest_framework/templates/rest_framework/base.html | 28 | ||||
| -rw-r--r-- | rest_framework/templates/rest_framework/login_base.html | 35 | ||||
| -rw-r--r-- | rest_framework/templatetags/rest_framework.py | 21 | ||||
| -rw-r--r-- | rest_framework/utils/formatting.py | 6 | ||||
| -rw-r--r-- | rest_framework/views.py | 10 |
11 files changed, 96 insertions, 46 deletions
diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 7c187639..8d82a4b9 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ ______ _____ _____ _____ __ """ __title__ = 'Django REST framework' -__version__ = '2.4.1' +__version__ = '2.4.2' __author__ = 'Tom Christie' __license__ = 'BSD 2-Clause' __copyright__ = 'Copyright 2011-2014 Tom Christie' diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index 5721a869..f3fec05e 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -344,7 +344,7 @@ class OAuth2Authentication(BaseAuthentication): user = token.user if not user.is_active: - msg = 'User inactive or deleted: %s' % user.username + msg = 'User inactive or deleted: %s' % user.get_username() raise exceptions.AuthenticationFailed(msg) return (user, token) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 9d707c9b..8e15345d 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -475,8 +475,12 @@ class CharField(WritableField): if isinstance(value, six.string_types): return value - if value is None and not self.allow_none: - return '' + if value is None: + if not self.allow_none: + return '' + else: + # Return None explicitly because smart_text(None) == 'None'. See #1834 for details + return None return smart_text(value) diff --git a/rest_framework/filters.py b/rest_framework/filters.py index e2080013..c580f935 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -56,7 +56,6 @@ class DjangoFilterBackend(BaseFilterBackend): class Meta: model = queryset.model fields = filter_fields - order_by = True return AutoFilterSet return None diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index d51ea929..1f5749f1 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -43,8 +43,9 @@ class DefaultObjectSerializer(serializers.Field): as the default. """ - def __init__(self, source=None, context=None): - # Note: Swallow context kwarg - only required for eg. ModelSerializer. + 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) @@ -82,7 +83,9 @@ class BasePaginationSerializer(serializers.Serializer): else: context_kwarg = {} - self.fields[results_field] = object_serializer(source='object_list', **context_kwarg) + self.fields[results_field] = object_serializer(source='object_list', + many=True, + **context_kwarg) class PaginationSerializer(BasePaginationSerializer): diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py index aa4fd3f1..c287908d 100644 --- a/rest_framework/parsers.py +++ b/rest_framework/parsers.py @@ -11,7 +11,7 @@ from django.http import QueryDict from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser from django.http.multipartparser import MultiPartParserError, parse_header, ChunkIter from django.utils import six -from rest_framework.compat import etree, yaml, force_text +from rest_framework.compat import etree, yaml, force_text, urlparse from rest_framework.exceptions import ParseError from rest_framework import renderers import json @@ -290,6 +290,22 @@ class FileUploadParser(BaseParser): try: meta = parser_context['request'].META disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION'].encode('utf-8')) - return force_text(disposition[1]['filename']) + filename_parm = disposition[1] + if 'filename*' in filename_parm: + return self.get_encoded_filename(filename_parm) + return force_text(filename_parm['filename']) except (AttributeError, KeyError): pass + + def get_encoded_filename(self, filename_parm): + """ + Handle encoded filenames per RFC6266. See also: + http://tools.ietf.org/html/rfc2231#section-4 + """ + encoded_filename = force_text(filename_parm['filename*']) + try: + charset, lang, filename = encoded_filename.split('\'', 2) + filename = urlparse.unquote(filename) + except (ValueError, LookupError): + filename = force_text(filename_parm['filename']) + return filename diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html index e54e3814..a84ccf26 100644 --- a/rest_framework/templates/rest_framework/base.html +++ b/rest_framework/templates/rest_framework/base.html @@ -5,14 +5,14 @@ <html> <head> {% block head %} - + {% block meta %} <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta name="robots" content="NONE,NOARCHIVE" /> {% endblock %} - + <title>{% block title %}Django REST framework{% endblock %}</title> - + {% block style %} {% block bootstrap_theme %} <link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap.min.css" %}"/> @@ -21,7 +21,7 @@ <link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/prettify.css" %}"/> <link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/default.css" %}"/> {% endblock %} - + {% endblock %} </head> @@ -44,17 +44,9 @@ <ul class="nav pull-right"> {% block userlinks %} {% if user.is_authenticated %} - <li class="dropdown"> - <a href="#" class="dropdown-toggle" data-toggle="dropdown"> - {{ user }} - <b class="caret"></b> - </a> - <ul class="dropdown-menu"> - <li>{% optional_logout request %}</li> - </ul> - </li> + {% optional_logout request user %} {% else %} - <li>{% optional_login request %}</li> + {% optional_login request %} {% endif %} {% endblock %} </ul> @@ -85,7 +77,7 @@ <div class="btn-group format-selection"> <a class="btn btn-primary js-tooltip" href='{{ request.get_full_path }}' rel="nofollow" title="Make a GET request on the {{ name }} resource">GET</a> - + <button class="btn btn-primary dropdown-toggle js-tooltip" data-toggle="dropdown" title="Specify a format for the GET request"> <span class="caret"></span> @@ -144,7 +136,7 @@ </div> {% if display_edit_forms %} - + {% if post_form or raw_data_post_form %} <div {% if post_form %}class="tabbable"{% endif %}> {% if post_form %} @@ -190,7 +182,7 @@ </div> </div> {% endif %} - + {% if put_form or raw_data_put_form or raw_data_patch_form %} <div {% if put_form %}class="tabbable"{% endif %}> {% if put_form %} @@ -246,7 +238,7 @@ {% endif %} </div> <!-- END Content --> - + <footer> {% block footer %} <p>Sponsored by <a href="http://dabapps.com/">DabApps</a>.</p> diff --git a/rest_framework/templates/rest_framework/login_base.html b/rest_framework/templates/rest_framework/login_base.html index 43860e53..8ab682ac 100644 --- a/rest_framework/templates/rest_framework/login_base.html +++ b/rest_framework/templates/rest_framework/login_base.html @@ -17,21 +17,44 @@ <div class="row-fluid"> <div> - <form action="{% url 'rest_framework:login' %}" class=" form-inline" method="post"> + <form action="{% url 'rest_framework:login' %}" role="form" method="post"> {% csrf_token %} - <div id="div_id_username" class="clearfix control-group"> + <div id="div_id_username" + class="clearfix control-group {% if form.username.errors %}error{% endif %}"> <div class="controls"> <Label class="span4">Username:</label> - <input style="height: 25px" type="text" name="username" maxlength="100" autocapitalize="off" autocorrect="off" class="textinput textInput" id="id_username"> + <input style="height: 25px" type="text" name="username" maxlength="100" + autocapitalize="off" + autocorrect="off" class="span12 textinput textInput" + id="id_username" required + {% if form.username.value %}value="{{ form.username.value }}"{% endif %}> + {% if form.username.errors %} + <p class="text-error"> + {{ form.username.errors|striptags }} + </p> + {% endif %} </div> </div> - <div id="div_id_password" class="clearfix control-group"> - <div class="controls"> + <div id="div_id_password" + class="clearfix control-group {% if form.password.errors %}error{% endif %}"> + <div class="controls"> <Label class="span4">Password:</label> - <input style="height: 25px" type="password" name="password" maxlength="100" autocapitalize="off" autocorrect="off" class="textinput textInput" id="id_password"> + <input style="height: 25px" type="password" name="password" maxlength="100" + autocapitalize="off" autocorrect="off" class="span12 textinput textInput" + id="id_password" required> + {% if form.password.errors %} + <p class="text-error"> + {{ form.password.errors|striptags }} + </p> + {% endif %} </div> </div> <input type="hidden" name="next" value="{{ next }}" /> + {% if form.non_field_errors %} + {% for error in form.non_field_errors %} + <div class="well well-small text-error" style="border: none">{{ error }}</div> + {% endfor %} + {% endif %} <div class="form-actions-no-box"> <input type="submit" name="submit" value="Log in" class="btn btn-primary" id="submit-id-submit"> </div> diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index b80a7d77..864d64dd 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -41,22 +41,31 @@ def optional_login(request): except NoReverseMatch: return '' - snippet = "<a href='%s?next=%s'>Log in</a>" % (login_url, escape(request.path)) + snippet = "<li><a href='{href}?next={next}'>Log in</a></li>".format(href=login_url, next=escape(request.path)) return snippet @register.simple_tag -def optional_logout(request): +def optional_logout(request, user): """ Include a logout snippet if REST framework's logout view is in the URLconf. """ try: logout_url = reverse('rest_framework:logout') except NoReverseMatch: - return '' - - snippet = "<a href='%s?next=%s'>Log out</a>" % (logout_url, escape(request.path)) - return snippet + return '<li class="navbar-text">{user}</li>'.format(user=user) + + snippet = """<li class="dropdown"> + <a href="#" class="dropdown-toggle" data-toggle="dropdown"> + {user} + <b class="caret"></b> + </a> + <ul class="dropdown-menu"> + <li><a href='{href}?next={next}'>Log out</a></li> + </ul> + </li>""" + + return snippet.format(user=user, href=logout_url, next=escape(request.path)) @register.simple_tag diff --git a/rest_framework/utils/formatting.py b/rest_framework/utils/formatting.py index 6d53aed1..470af51b 100644 --- a/rest_framework/utils/formatting.py +++ b/rest_framework/utils/formatting.py @@ -2,11 +2,12 @@ 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 -import re + +from rest_framework.compat import apply_markdown, force_text def remove_trailing_string(content, trailing): @@ -28,6 +29,7 @@ def dedent(content): as it fails to dedent multiline docstrings that include unindented text on the initial line. """ + content = force_text(content) whitespace_counts = [len(line) - len(line.lstrip(' ')) for line in content.splitlines()[1:] if line.lstrip()] diff --git a/rest_framework/views.py b/rest_framework/views.py index 23df3443..38346ab7 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -103,7 +103,9 @@ class APIView(View): """ view = super(APIView, cls).as_view(**initkwargs) view.cls = cls - return view + # Note: session based authentication is explicitly CSRF validated, + # all other authentication is CSRF exempt. + return csrf_exempt(view) @property def allowed_methods(self): @@ -371,9 +373,9 @@ class APIView(View): response.exception = True return response - # Note: session based authentication is explicitly CSRF validated, - # all other authentication is CSRF exempt. - @csrf_exempt + # Note: Views are made CSRF exempt from within `as_view` as to prevent + # accidental removal of this exemption in cases where `dispatch` needs to + # be overridden. def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, |
