[Python-modules-commits] [python-django-braces] 03/10: Import python-django-braces_1.9.0.orig.tar.gz

Michael Fladischer fladi at moszumanska.debian.org
Sun Jun 19 10:19:40 UTC 2016


This is an automated email from the git hooks/post-receive script.

fladi pushed a commit to branch master
in repository python-django-braces.

commit 2057b82f2eb7b34b1ad6644e6fabb52f82395b30
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date:   Fri Jun 17 13:32:28 2016 +0200

    Import python-django-braces_1.9.0.orig.tar.gz
---
 .gitignore                  |   1 +
 .travis.yml                 |  28 +++++++-----
 CONTRIBUTORS.txt            |   3 ++
 README.md                   |  12 +++---
 braces/__init__.py          |   2 +-
 braces/views/_access.py     |  36 +++++++++-------
 braces/views/_ajax.py       |  19 ++++----
 braces/views/_forms.py      |   5 +--
 docs/access.rst             |   8 ++--
 docs/changelog.rst          |  27 +++++++++---
 docs/conf.py                |   4 +-
 docs/contributing.rst       |   2 +
 docs/form.rst               |   4 +-
 docs/other.rst              |  78 +++++++++++++++++++++++++++++++--
 requirements.txt            |   1 -
 setup.py                    |  13 ++++--
 tests/test_access_mixins.py | 103 ++++++++++++++++++++++++++++++++++++++------
 tests/test_ajax_mixins.py   |  79 +++++++++++++++++----------------
 tests/test_other_mixins.py  |  10 ++---
 tests/views.py              |   7 +++
 tox.ini                     |  12 +++---
 21 files changed, 326 insertions(+), 128 deletions(-)

diff --git a/.gitignore b/.gitignore
index f07bd9b..1f73f3e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@
 /.coverage.xml
 /htmlcov
 /.tox
+/.cache
 dist/
 .idea
 build/
diff --git a/.travis.yml b/.travis.yml
index 402512f..1e4b574 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,29 +1,37 @@
 language: python
 services: sqlite
 env:
-    - DJANGO='django>=1.4,<1.5'
     - DJANGO='django>=1.5,<1.6'
     - DJANGO='django>=1.6,<1.7'
     - DJANGO='django>=1.7,<1.8'
+    - DJANGO='django>=1.8,<1.9'
+    - DJANGO='django>=1.9,<1.10'
 python:
+    - 3.5
     - 3.4
     - 3.3
+    - 3.2
     - 2.7
-    - 2.6
-    - pypy
-    - pypy3
 install:
     - pip install $DJANGO
     - python setup.py install
-    - pip install -r requirements.txt
+    - pip install factory_boy mock pytest-django
 script: py.test tests/
 matrix:
     exclude:
+        - python: 3.2
+          env: DJANGO='django>=1.8,<1.9'
+        - python: 3.2
+          env: DJANGO='django>=1.9,<1.10'
         - python: 3.3
-          env: DJANGO='django>=1.4,<1.5'
+          env: DJANGO='django>=1.8,<1.9'
+        - python: 3.3
+          env: DJANGO='django>=1.9,<1.10'
         - python: 3.4
           env: DJANGO='django>=1.4,<1.5'
-        - python: pypy3
-          env: DJANGO='django>=1.4,<1.5'
-        - python: 2.6
-          env: DJANGO='django>=1.7,<1.8'
+        - python: 3.5
+          env: DJANGO='django>=1.5,<1.6'
+        - python: 3.5
+          env: DJANGO='django>=1.6,<1.7'
+        - python: 3.5
+          env: DJANGO='django>=1.7,<1.8'
\ No newline at end of file
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 6803066..9a8a712 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -26,6 +26,9 @@ Direct Contributors
 * Kit Sunde
 * Ben Cardy
 * Rag Sagar.V
+* Lacey Williams Henschel 
+* Gregory Shikhman
+* Mike Bryant
 
 Other Contributors
 ==================
diff --git a/README.md b/README.md
index 406349d..2e2cbe5 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,11 @@
 # django-braces
 Mixins for Django's class-based views.
 
-[![Latest drone.io status](https://drone.io/github.com/brack3t/django-braces/status.png)](https://drone.io/github.com/brack3t/django-braces)
-[![Latest PyPI version](https://pypip.in/v/django-braces/badge.png)](https://crate.io/packages/django-braces/)
-[![Number of PyPI downloads](https://pypip.in/d/django-braces/badge.png)](https://crate.io/packages/django-braces/)
-[![Stories in Ready](https://badge.waffle.io/brack3t/django-braces.png)](http://waffle.io/brack3t/django-braces)
+[![Latest Travis CI status](https://travis-ci.org/brack3t/django-braces.svg)](https://travis-ci.org/brack3t/django-braces)
+[![PyPI version](https://badge.fury.io/py/django-braces.png)](http://badge.fury.io/py/django-braces)
 
 ## Documentation
-[Read The Docs](http://django-braces.readthedocs.org/en/latest/index.html)
+[Read The Docs](http://django-braces.readthedocs.io/en/latest/index.html)
 
 ## Installation
 Install from PyPI with `pip`:
@@ -21,7 +19,7 @@ Install from PyPI with `pip`:
 
 ## Contributing
 
-See our [contribution guide](https://django-braces.readthedocs.org/en/latest/contributing.html)
+See our [contribution guide](https://django-braces.readthedocs.io/en/latest/contributing.html)
 
 Add yourself to `CONTRIBUTORS.txt` if you want.
 
@@ -34,7 +32,7 @@ Or test with `tox` if you have `tox` installed.
 
 ## Change Log
 
-[Changelog on Read The Docs](https://django-braces.readthedocs.org/en/latest/changelog.html)
+[Changelog on Read The Docs](https://django-braces.readthedocs.io/en/latest/changelog.html)
 
 ## Use Django 1.4.x?
 
diff --git a/braces/__init__.py b/braces/__init__.py
index c809756..bd7667d 100644
--- a/braces/__init__.py
+++ b/braces/__init__.py
@@ -9,7 +9,7 @@ Several mixins for making Django's generic class-based views more useful.
 """
 
 __title__ = 'braces'
-__version__ = '1.8.0'
+__version__ = '1.9.0'
 __author__ = 'Kenneth Love and Chris Jones'
 __license__ = 'BSD 3-clause'
 __copyright__ = 'Copyright 2013 Kenneth Love and Chris Jones'
diff --git a/braces/views/_access.py b/braces/views/_access.py
index 32f93c5..285d37e 100644
--- a/braces/views/_access.py
+++ b/braces/views/_access.py
@@ -1,7 +1,6 @@
 import inspect
 import datetime
 import re
-import six
 
 from django.conf import settings
 from django.contrib.auth import REDIRECT_FIELD_NAME
@@ -10,6 +9,7 @@ from django.core.exceptions import ImproperlyConfigured, PermissionDenied
 from django.http import (HttpResponseRedirect, HttpResponsePermanentRedirect,
                          Http404, HttpResponse)
 from django.shortcuts import resolve_url
+from django.utils import six
 from django.utils.encoding import force_text
 from django.utils.timezone import now
 
@@ -57,15 +57,19 @@ class AccessMixin(object):
         return self.redirect_field_name
 
     def handle_no_permission(self, request):
-        if self.raise_exception and not self.redirect_unauthenticated_users:
-            if (inspect.isclass(self.raise_exception)
-                    and issubclass(self.raise_exception, Exception)):
-                raise self.raise_exception
-            if callable(self.raise_exception):
-                ret = self.raise_exception(request)
-                if isinstance(ret, (HttpResponse, StreamingHttpResponse)):
-                    return ret
-            raise PermissionDenied
+        if self.raise_exception:
+            if (self.redirect_unauthenticated_users
+                    and not self.request.user.is_authenticated()):
+                return self.no_permissions_fail(request)
+            else:
+                if (inspect.isclass(self.raise_exception)
+                        and issubclass(self.raise_exception, Exception)):
+                    raise self.raise_exception
+                if callable(self.raise_exception):
+                    ret = self.raise_exception(request)
+                    if isinstance(ret, (HttpResponse, StreamingHttpResponse)):
+                        return ret
+                raise PermissionDenied
 
         return self.no_permissions_fail(request)
 
@@ -430,8 +434,10 @@ class RecentLoginRequiredMixin(LoginRequiredMixin):
         resp = super(RecentLoginRequiredMixin, self).dispatch(
             request, *args, **kwargs)
 
-        delta = datetime.timedelta(seconds=self.max_last_login_delta)
-        if now() > (request.user.last_login + delta):
-            return logout_then_login(request, self.get_login_url())
-        else:
-            return resp
+        if resp.status_code == 200:
+            delta = datetime.timedelta(seconds=self.max_last_login_delta)
+            if now() > (request.user.last_login + delta):
+                return logout_then_login(request, self.get_login_url())
+            else:
+                return resp
+        return resp
diff --git a/braces/views/_ajax.py b/braces/views/_ajax.py
index fbec601..2cef960 100644
--- a/braces/views/_ajax.py
+++ b/braces/views/_ajax.py
@@ -1,9 +1,10 @@
-import six
+from __future__ import unicode_literals
 
 from django.core import serializers
 from django.core.exceptions import ImproperlyConfigured
 from django.core.serializers.json import DjangoJSONEncoder
 from django.http import HttpResponse, HttpResponseBadRequest
+from django.utils import six
 
 ## Django 1.5+ compat
 try:
@@ -29,12 +30,12 @@ class JSONResponseMixin(object):
                 '{0} is missing a content type. Define {0}.content_type, '
                 'or override {0}.get_content_type().'.format(
                     self.__class__.__name__))
-        return self.content_type or u"application/json"
+        return self.content_type or "application/json"
 
     def get_json_dumps_kwargs(self):
         if self.json_dumps_kwargs is None:
             self.json_dumps_kwargs = {}
-        self.json_dumps_kwargs.setdefault(u'ensure_ascii', False)
+        self.json_dumps_kwargs.setdefault('ensure_ascii', False)
         return self.json_dumps_kwargs
 
     def render_json_response(self, context_dict, status=200):
@@ -45,7 +46,7 @@ class JSONResponseMixin(object):
         json_context = json.dumps(
             context_dict,
             cls=self.json_encoder_class,
-            **self.get_json_dumps_kwargs()).encode(u'utf-8')
+            **self.get_json_dumps_kwargs()).encode('utf-8')
         return HttpResponse(json_context,
                             content_type=self.get_content_type(),
                             status=status)
@@ -55,7 +56,7 @@ class JSONResponseMixin(object):
         Serializes objects using Django's builtin JSON serializer. Additional
         kwargs can be used the same way for django.core.serializers.serialize.
         """
-        json_data = serializers.serialize(u"json", objects, **kwargs)
+        json_data = serializers.serialize("json", objects, **kwargs)
         return HttpResponse(json_data, content_type=self.get_content_type())
 
 
@@ -69,7 +70,7 @@ class AjaxResponseMixin(object):
         request_method = request.method.lower()
 
         if request.is_ajax() and request_method in self.http_method_names:
-            handler = getattr(self, u"{0}_ajax".format(request_method),
+            handler = getattr(self, "{0}_ajax".format(request_method),
                               self.http_method_not_allowed)
             self.request = request
             self.args = args
@@ -112,7 +113,7 @@ class JsonRequestResponseMixin(JSONResponseMixin):
                     {'message': 'Thanks!'})
     """
     require_json = False
-    error_response_dict = {u"errors": [u"Improperly formatted request"]}
+    error_response_dict = {"errors": ["Improperly formatted request"]}
 
     def render_bad_request_response(self, error_dict=None):
         if error_dict is None:
@@ -121,13 +122,13 @@ class JsonRequestResponseMixin(JSONResponseMixin):
             error_dict,
             cls=self.json_encoder_class,
             **self.get_json_dumps_kwargs()
-        ).encode(u'utf-8')
+        ).encode('utf-8')
         return HttpResponseBadRequest(
             json_context, content_type=self.get_content_type())
 
     def get_request_json(self):
         try:
-            return json.loads(self.request.body.decode(u'utf-8'))
+            return json.loads(self.request.body.decode('utf-8'))
         except ValueError:
             return None
 
diff --git a/braces/views/_forms.py b/braces/views/_forms.py
index 955d89d..e209820 100644
--- a/braces/views/_forms.py
+++ b/braces/views/_forms.py
@@ -1,8 +1,7 @@
-import six
-
 from django.contrib import messages
 from django.core.exceptions import ImproperlyConfigured
 from django.core.urlresolvers import reverse
+from django.utils import six
 from django.utils.decorators import method_decorator
 from django.utils.encoding import force_text
 from django.utils.functional import curry, Promise
@@ -50,7 +49,7 @@ class SuccessURLRedirectListMixin(object):
         # Return the reversed success url.
         if self.success_list_url is None:
             raise ImproperlyConfigured(
-                '{0} is missing a succes_list_url '
+                '{0} is missing a success_list_url '
                 'name to reverse and redirect to. Define '
                 '{0}.success_list_url or override '
                 '{0}.get_success_url().'.format(self.__class__.__name__))
diff --git a/docs/access.rst b/docs/access.rst
index 5541f0c..49ad126 100644
--- a/docs/access.rst
+++ b/docs/access.rst
@@ -90,7 +90,7 @@ In normal use of this mixin, :ref:`LoginRequiredMixin` comes first, then the ``P
         permission_required = "auth.change_user"
         template_name = "path/to/template.html"
 
-The ``PermissionRequiredMixin`` also offers a ``check_permssions`` method that should be overridden if you need custom permissions checking.
+The ``PermissionRequiredMixin`` also offers a ``check_permissions`` method that should be overridden if you need custom permissions checking.
 
 
 .. _MultiplePermissionsRequiredMixin:
@@ -120,7 +120,7 @@ The ``MultiplePermissionsRequiredMixin`` is a more powerful version of the :ref:
             "any": ("blog.delete_post", "user.change_user")
         }
 
-The ``MultiplePermissionsRequiredMixin`` also offers a ``check_permssions`` method that should be overridden if you need custom permissions checking.
+The ``MultiplePermissionsRequiredMixin`` also offers a ``check_permissions`` method that should be overridden if you need custom permissions checking.
 
 
 .. _GroupRequiredMixin:
@@ -326,7 +326,7 @@ Similar to :ref:`SuperuserRequiredMixin`, this mixin allows you to require a use
         template_name = u"path/to/template.html"
 
 
-.. _SSLRequiredMixin
+.. _SSLRequiredMixin:
 
 SSLRequiredMixin
 ----------------
@@ -392,5 +392,5 @@ This mixin requires a user to have logged in within a certain number of seconds.
 
 .. _Daniel Sokolowski: https://github.com/danols
 .. _code here: https://github.com/lukaszb/django-guardian/issues/48
-.. _user_passes_test: https://docs.djangoproject.com/en/1.6/topics/auth/default/#django.contrib.auth.decorators.user_passes_test
+.. _user_passes_test: https://docs.djangoproject.com/en/dev/topics/auth/default/#django.contrib.auth.decorators.user_passes_test
 .. _settings.LOGIN_REDIRECT_URL: https://docs.djangoproject.com/en/1.6/ref/settings/#login-redirect-url
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 5fcb8cd..d7fefee 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -4,9 +4,26 @@
 Changelog
 =========
 
+* :release:`1.9.0 <2016-5-31>`
+* :bug:`208 major` Fixed errors from combining certain access mixins.
+* :bug:`196 major` Refactor how users without permissions are handled.
+* :bug:`181 major` Fixed redirect loops based on user permissions.
+* :bug:`161 major` Fixed redirect loop for users without proper groups for ``MultipleGroupRequiredMixin`` and ``GroupRequiredMixin``.
+* :support:`209` Fixed link to Django documentation for ``user_passes_test`` decorator.
+* :feature:`203` Use Django's supplied version of ``six`` to remove an external dependency.
+* :support:`202` Fixed typo in ``PermissionsRequiredMixin`` and ``MultiplePermissionsRequiredMixin``.
+* :support:`201` Fixed typo in ``SuccessURLRedirectListMixin``.
+* :support:`192` Added example for ``OrderableListView``.
+* :release:`1.8.1 <2015-7-12>`
+* :bug:`176` Only check time delta for authenticated users in :ref:`RecentLoginRequiredMixin`. 
+* :bug:`-` Changed :ref:`JsonRequestResponseMixin` docs to not use `ugettext_lazy`.
+* :bug:`-` Updated tests to include Python 3.2.
+* :bug:`185` Removed `u` prefixes to allow Python 3.2 support.
+* :support:`-` Added note to docs about Python and Django versions used in tests.
+* :bug:`-` Fix small issue in docs for :ref:JsonResponseMixin. The accepted kwarg for the render_to_response method is status not status_code.
 * :release:`1.8.0 <2015-04-16>`
 * :support:`145` Allow custom exceptions to be raised by all AccessMixins.
-* :feature:`171` New :ref:`SSLRequiredMixin`. Redirect http -> https.
+* :feature:`171` New ``SSLRequiredMixin``. Redirect http -> https.
 * :feature:`138` New :ref:`RecentLoginRequiredMixin` to require user sessions to have a given freshness.
 * :bug:`164 major` Use `resolve_url` to handle `LOGIN_REDIRECT_URL`s in `settings.py` that are just URL names.
 * :bug:`130 major` New attribute on :ref:`JSONResponseMixin` to allow setting a custom JSON encoder class.
@@ -65,13 +82,13 @@ Changelog
 * :support:`-` Fixes to documentation.
 * :support:`-` Tests to cover new additions and changes.
 * :release:`0.2.3 <2013-02-22>`
-* :support:`30` Tests for all mixins (from rafales).
-* :feature:`26` New :ref:`CsrfExemptMixin` for marking views as being CSRF exempt (from jarcoal).
-* :support:`-` Some documentation updates and a spelling error correction (from shabda).
+* :support:`30 backported` Tests for all mixins (from rafales).
+* :feature:`26 backported` New :ref:`CsrfExemptMixin` for marking views as being CSRF exempt (from jarcoal).
+* :support:`- backported` Some documentation updates and a spelling error correction (from shabda).
 * :bug:`-` :ref:`SuccessURLRedirectListMixin` raises ``ImproperlyConfigured`` if no ``success_list_url`` attribute is supplied (from kennethlove).
 * :release:`0.2.2 <2013-01-21>`
 * :bug:`25` Try importing the built-in ``json`` module first, drop back to Django if necessary.
-* :support:`-` Django 1.5 compatibility.
+* :support:`- backported` Django 1.5 compatibility.
 * :release:`0.2.1 <2012-12-10>`
 * :bug:`21 major` Fixed signature of :ref:`UserFormKwargsMixin` ``.get_form_kwargs``
 * :feature:`22` Updated :ref:`JSONResponseMixin` to work with non-ASCII characters and other datatypes (such as datetimes)
diff --git a/docs/conf.py b/docs/conf.py
index d2e6ab9..3b0e84d 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -33,6 +33,8 @@ extensions = ['releases']
 
 releases_issue_uri = "https://github.com/brack3t/django-braces/issues/%s"
 releases_release_uri = "https://github.com/brack3t/django-braces/tree/%s"
+releases_unstable_prehistory = True
+# releases_debug = True
 
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
@@ -98,7 +100,7 @@ pygments_style = 'sphinx'
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
-html_theme = 'alabaster'
+html_theme = 'default'
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
diff --git a/docs/contributing.rst b/docs/contributing.rst
index 48682bc..7b35fa8 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -49,3 +49,5 @@ skipping.
 We try to keep the project at 100% test coverage but know this isn't something
 we can achieve forever. So long as your tests cover your contribution 80% or
 better, we're happy to try and bump up that last bit, or just accept the code.
+
+We currently test Braces against late (usually latest) versions of Python 2.6, 2.7, 3.2, 3.3, and 3.4. We also test against the latest released version of Django from 1.5 to 1.8.
diff --git a/docs/form.rst b/docs/form.rst
index f339896..d656126 100644
--- a/docs/form.rst
+++ b/docs/form.rst
@@ -36,7 +36,7 @@ UserFormKwargsMixin
 
 A common pattern in Django is to have forms that are customized to a user. To custom tailor the form for users, you have to pass that user instance into the form and, based on their permission level or other details, change certain fields or add specific options within the forms ``__init__`` method.
 
-This mixin automates the process of overloading the ``get_form_kwargs`` (this method is available in any generic view which handles a form) method and stuffs the user instance into the form kwargs. The user can then be ``pop()``ed off in the form. **Always** remember to pop the user from the kwargs before calling ``super()`` on your form, otherwise the form will get an unexpected keyword argument.
+This mixin automates the process of overloading the ``get_form_kwargs`` (this method is available in any generic view which handles a form) method and stuffs the user instance into the form kwargs. The user can then be ``pop()``\ ped off in the form. **Always** remember to pop the user from the kwargs before calling ``super()`` on your form, otherwise the form will get an unexpected keyword argument.
 
 Usage
 ^^^^^
@@ -63,7 +63,7 @@ UserKwargModelFormMixin
 -----------------------
 
 The ``UserKwargModelFormMixin`` is a form mixin to go along with our :ref:`UserFormKwargsMixin`.
-This becomes the first inherited class of our forms that receive the ``user`` keyword argument. With this mixin, the ``pop()``ing of the ``user`` is automated and no longer has to be done manually on every form that needs this behavior. 
+This becomes the first inherited class of our forms that receive the ``user`` keyword argument. With this mixin, the ``pop()``\ ping of the ``user`` is automated and no longer has to be done manually on every form that needs this behavior. 
 
 Usage
 ^^^^^
diff --git a/docs/other.rst b/docs/other.rst
index e1ba2c7..1031229 100644
--- a/docs/other.rst
+++ b/docs/other.rst
@@ -151,7 +151,7 @@ JSONResponseMixin
 -----------------
 
 .. versionchanged:: 1.1
-    ``render_json_response`` now accepts a ``status_code`` keyword argument.
+    ``render_json_response`` now accepts a ``status`` keyword argument.
     ``json_dumps_kwargs`` class-attribute and ``get_json_dumps_kwargs`` method to provide arguments to the ``json.dumps()`` method.
 
 A simple mixin to handle very simple serialization as a response to the browser.
@@ -275,7 +275,6 @@ It extends :ref:`JSONResponseMixin`, so those utilities are available as well.
 
 ::
 
-    from django.utils.translation import ugettext_lazy as _
     from django.views.generic import View
 
     from braces import views
@@ -289,11 +288,11 @@ It extends :ref:`JSONResponseMixin`, so those utilities are available as well.
                 toppings = self.request_json[u"toppings"]
             except KeyError:
                 error_dict = {u"message":
-                   _(u"your order must include a burrito AND toppings")}
+                   u"your order must include a burrito AND toppings"}
                 return self.render_bad_request_response(error_dict)
             place_order(burrito, toppings)
             return self.render_json_response(
-                {u"message": _(u"Your order has been placed!")})
+                {u"message": u"Your order has been placed!"})
 
 
 .. _AjaxResponseMixin:
@@ -379,6 +378,77 @@ The ``orderable_columns`` restriction is here in order to stop your users from l
 Example url: `http://127.0.0.1:8000/articles/?order_by=title&ordering=asc`
 
 
+**Front-end Example Usage**
+
+If you're using bootstrap you could create a template like the following:
+
+.. code:: html
+
+    <div class="table-responsive">
+        <table class="table table-striped table-bordered">
+            <tr>
+                <th><a class="order-by-column" data-column="id" href="#">ID</a></th>
+                <th><a class="order-by-column" data-column="title" href="#">Title</a></th>
+            </tr>
+            {% for object in object_list %}
+                <tr>
+                    <td>{{ object.id }}</td>
+                    <td>{{ object.title }}</td>
+                </tr>
+            {% endfor %}
+        </table>
+    </div>
+
+    <script>
+    function setupOrderedColumns(order_by, orderin) {
+
+        $('.order-by-column').each(function() {
+
+            var $el = $(this),
+                column_name = $el.data('column'),
+                href = location.href,
+                next_order = 'asc',
+                has_query_string = (href.indexOf('?') !== -1),
+                order_by_param,
+                ordering_param;
+
+            if (order_by === column_name) {
+                $el.addClass('current');
+                $el.addClass(ordering);
+                $el.append('<span class="caret"></span>');
+                if (ordering === 'asc') {
+                    $el.addClass('dropup');
+                    next_order = 'desc';
+                }
+            }
+
+            order_by_param = "order_by=" + column_name;
+            ordering_param = "ordering=" + next_order;
+
+            if (!has_query_string) {
+                href = '?' + order_by_param + '&' + ordering_param;
+            } else {
+                if (href.match(/ordering=(asc|desc)/)) {
+                    href = href.replace(/ordering=(asc|desc)/, ordering_param);
+                } else {
+                    href += '&' + ordering_param;
+                }
+
+                if (href.match(/order_by=[_\w]+/)) {
+                    href = href.replace(/order_by=([_\w]+)/, order_by_param);
+                } else {
+                    href += '&' + order_by_param;
+                }
+
+            }
+
+            $el.attr('href', href);
+
+        });
+    }
+    setupOrderedColumns('{{ order_by }}', '{{ ordering }}');
+    </script>
+
 .. _CanonicalSlugDetailMixin:
 
 CanonicalSlugDetailMixin
diff --git a/requirements.txt b/requirements.txt
index 2e15c4e..29f8d9e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,5 +2,4 @@ factory_boy
 mock
 pytest-django
 pytest-cov
-six
 coverage
diff --git a/setup.py b/setup.py
index 9287a2a..9a49f6b 100644
--- a/setup.py
+++ b/setup.py
@@ -14,17 +14,24 @@ setup(
     license="BSD",
     packages=["braces"],
     zip_safe=False,
-    install_requires=["six"],
     include_package_data=True,
     classifiers=[
         "Programming Language :: Python",
         "Topic :: Software Development :: Libraries :: Python Modules",
-        "Framework :: Django",
+        "License :: OSI Approved :: BSD License",
         "Environment :: Web Environment",
         "Development Status :: 5 - Production/Stable",
         "Programming Language :: Python :: 2.6",
         "Programming Language :: Python :: 2.7",
+        "Programming Language :: Python :: 3.2",
         "Programming Language :: Python :: 3.3",
-        "Programming Language :: Python :: 3.4"
+        "Programming Language :: Python :: 3.4",
+        "Programming Language :: Python :: 3.5",
+        "Framework :: Django",
+        "Framework :: Django :: 1.5",
+        "Framework :: Django :: 1.6",
+        "Framework :: Django :: 1.7",
+        "Framework :: Django :: 1.8",
+        "Framework :: Django :: 1.9"
     ],
 )
diff --git a/tests/test_access_mixins.py b/tests/test_access_mixins.py
index 407b0d4..99c8abc 100644
--- a/tests/test_access_mixins.py
+++ b/tests/test_access_mixins.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-from __future__ import absolute_import
+from __future__ import absolute_import, unicode_literals
 
 import pytest
 import datetime
@@ -18,7 +18,8 @@ from .views import (PermissionRequiredView, MultiplePermissionsRequiredView,
                     SuperuserRequiredView, StaffuserRequiredView,
                     LoginRequiredView, GroupRequiredView, UserPassesTestView,
                     UserPassesTestNotImplementedView, AnonymousRequiredView,
-                    SSLRequiredView, RecentLoginRequiredView)
+                    SSLRequiredView, RecentLoginRequiredView,
+                    UserPassesTestLoginRequiredView)
 
 
 class _TestAccessBasicsMixin(TestViewHelper):
@@ -57,7 +58,7 @@ class _TestAccessBasicsMixin(TestViewHelper):
         user = self.build_unauthorized_user()
         self.client.login(username=user.username, password='asdf1234')
         resp = self.client.get(self.view_url)
-        self.assertRedirects(resp, u'/accounts/login/?next={0}'.format(
+        self.assertRedirects(resp, '/accounts/login/?next={0}'.format(
             self.view_url))
 
     def test_raise_permission_denied(self):
@@ -148,12 +149,12 @@ class _TestAccessBasicsMixin(TestViewHelper):
         req = self.build_request(user=user, path=self.view_url)
         resp = self.dispatch_view(req, login_url='/login/')
         self.assertEqual(
-            u'/login/?next={0}'.format(self.view_url),
+            '/login/?next={0}'.format(self.view_url),
             resp['Location'])
 
         # Test with reverse_lazy
         resp = self.dispatch_view(req, login_url=reverse_lazy('headline'))
-        self.assertEqual(u'/headline/?next={0}'.format(
+        self.assertEqual('/headline/?next={0}'.format(
             self.view_url), resp['Location'])
 
     def test_custom_redirect_field_name(self):
@@ -163,7 +164,7 @@ class _TestAccessBasicsMixin(TestViewHelper):
         user = self.build_unauthorized_user()
         req = self.build_request(user=user, path=self.view_url)
         resp = self.dispatch_view(req, redirect_field_name='foo')
-        expected_url = u'/accounts/login/?foo={0}'.format(self.view_url)
+        expected_url = '/accounts/login/?foo={0}'.format(self.view_url)
         self.assertEqual(expected_url, resp['Location'])
 
     @override_settings(LOGIN_URL=None)
@@ -195,7 +196,7 @@ class _TestAccessBasicsMixin(TestViewHelper):
         user = self.build_unauthorized_user()
         self.client.login(username=user.username, password='asdf1234')
         resp = self.client.get(self.view_url)
-        self.assertRedirects(resp, u'/auth/login/?next={0}'.format(
+        self.assertRedirects(resp, '/auth/login/?next={0}'.format(
             self.view_url))
 
     def test_redirect_unauthenticated(self):
@@ -246,6 +247,63 @@ class TestLoginRequiredMixin(TestViewHelper, test.TestCase):
         assert resp['Location'] == '/accounts/login/?next=/login_required/'
 
 
+class TestChainedLoginRequiredMixin(TestViewHelper, test.TestCase):
+    """
+    Tests for LoginRequiredMixin combined with another AccessMixin.
+    """
+    view_class = UserPassesTestLoginRequiredView
+    view_url = '/chained_view/'
+
+    def assert_redirect_to_login(self, response):
+        """
+        Check that the response is a redirect to the login view.
+        """
+        assert response.status_code == 302
+        assert response['Location'] == '/accounts/login/?next=/chained_view/'
+
+    def test_anonymous(self):
+        """
+        Check that anonymous users redirect to login by default.
+        """
+        resp = self.dispatch_view(
+            self.build_request(path=self.view_url))
+        self.assert_redirect_to_login(resp)
+
+    def test_anonymous_raises_exception(self):
+        """
+        Check that when anonymous users hit a view that has only
+        raise_exception set, they get a PermissionDenied.
+        """
+        with self.assertRaises(PermissionDenied):
+            self.dispatch_view(
+                self.build_request(path=self.view_url), raise_exception=True)
+
+    def test_authenticated_raises_exception(self):
+        """
+        Check that when authenticated users hit a view that has raise_exception
+        set, they get a PermissionDenied.
+        """
+        user = UserFactory()
+        with self.assertRaises(PermissionDenied):
+            self.dispatch_view(
+                self.build_request(path=self.view_url, user=user),
+                raise_exception=True)
+        with self.assertRaises(PermissionDenied):
+            self.dispatch_view(
+                self.build_request(path=self.view_url, user=user),
+                raise_exception=True, redirect_unauthenticated_users=True)
+
+    def test_anonymous_redirects(self):
+        """
+        Check that anonymous users are redirected to login when raise_exception
+        is overridden by redirect_unauthenticated_users.
+        """
+        resp = self.dispatch_view(
+            self.build_request(path=self.view_url), raise_exception=True,
+            redirect_unauthenticated_users=True)
+        self.assert_redirect_to_login(resp)
+
+
 class TestAnonymousRequiredMixin(TestViewHelper, test.TestCase):
     """
     Tests for AnonymousRequiredMixin.
@@ -350,7 +408,7 @@ class TestMultiplePermissionsRequiredMixin(
             user = UserFactory(permissions=permissions)
             self.client.login(username=user.username, password='asdf1234')
             resp = self.client.get(url)
-            self.assertRedirects(resp, u'/accounts/login/?next={0}'.format(
+            self.assertRedirects(resp, '/accounts/login/?next={0}'.format(
                 url))
 
     def test_invalid_permissions(self):
@@ -508,14 +566,14 @@ class TestGroupRequiredMixin(_TestAccessBasicsMixin, test.TestCase):
             view.get_group_required()
 
     def test_with_unicode(self):
-        self.view_class.group_required = u'niño'
-        self.assertEqual(u'niño', self.view_class.group_required)
+        self.view_class.group_required = 'niño'
+        self.assertEqual('niño', self.view_class.group_required)
 
         user = self.build_authorized_user()
         group = user.groups.all()[0]
-        group.name = u'niño'
+        group.name = 'niño'
         group.save()
-        self.assertEqual(u'niño', user.groups.all()[0].name)
+        self.assertEqual('niño', user.groups.all()[0].name)
 
         self.client.login(username=user.username, password='asdf1234')
         resp = self.client.get(self.view_url)
@@ -571,8 +629,21 @@ class TestSSLRequiredMixin(test.TestCase):
     view_class = SSLRequiredView
     view_url = '/sslrequired/'
 
+    @pytest.mark.skipif(DJANGO_VERSION[:2] < (1, 9),
+                        reason='Django 1.9 and above behave differently')
+    def test_ssl_redirection_django_19_up(self):
+        self.view_url = 'https://testserver' + self.view_url
+        self.view_class.raise_exception = False
+        resp = self.client.get(self.view_url)
+        self.assertRedirects(resp, self.view_url, status_code=301)
+        resp = self.client.get(self.view_url, follow=True)
+        self.assertEqual(200, resp.status_code)
+        self.assertEqual('https', resp.request.get('wsgi.url_scheme'))
+
     @pytest.mark.skipif(DJANGO_VERSION[:2] < (1, 7),
-                        reason='Djanog 1.6 and below behave this differently')
+                        reason='Django 1.6 and below behave differently')
+    @pytest.mark.skipif(DJANGO_VERSION[:2] > (1, 8),
+                        reason='Django 1.6 and below behave differently')
     def test_ssl_redirection_django_17_up(self):
         self.view_class.raise_exception = False
         resp = self.client.get(self.view_url)
@@ -645,3 +716,9 @@ class TestRecentLoginRequiredMixin(test.TestCase):
         self.client.login(username=user.username, password='asdf1234')
         resp = self.client.get(self.outdated_view_url)
         assert resp.status_code == 302
+        
+    def test_not_logged_in(self):
+        last_login = datetime.datetime.now()
+        user = UserFactory(last_login=last_login)
+        resp = self.client.get(self.recent_view_url)
+        assert resp.status_code != 200
diff --git a/tests/test_ajax_mixins.py b/tests/test_ajax_mixins.py
index 9e1a0ba..b5e95da 100644
--- a/tests/test_ajax_mixins.py
+++ b/tests/test_ajax_mixins.py
@@ -1,4 +1,4 @@
-from __future__ import absolute_import
+from __future__ import absolute_import, unicode_literals
 
 import mock
 
@@ -20,7 +20,7 @@ class TestAjaxResponseMixin(TestViewHelper, test.TestCase):
     """
     Tests for AjaxResponseMixin.
     """
-    methods = [u'get', u'post', u'put', u'delete']
+    methods = ['get', 'post', 'put', 'delete']
 
     def test_xhr(self):
         """
@@ -29,9 +29,8 @@ class TestAjaxResponseMixin(TestViewHelper, test.TestCase):
         # AjaxResponseView returns 'AJAX_OK' when requested with XmlHttpRequest
         for m in self.methods:
             fn = getattr(self.client, m)
-            resp = fn(u'/ajax_response/',
-                      HTTP_X_REQUESTED_WITH=u'XMLHttpRequest')
-            assert force_text(resp.content) == u'AJAX_OK'
+            resp = fn('/ajax_response/', HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+            assert force_text(resp.content) == 'AJAX_OK'
 
     def test_not_xhr(self):
         """
@@ -40,18 +39,18 @@ class TestAjaxResponseMixin(TestViewHelper, test.TestCase):
         """
         for m in self.methods:
             fn = getattr(self.client, m)
-            resp = fn(u'/ajax_response/')
-            assert force_text(resp.content) == u'OK'
+            resp = fn('/ajax_response/')
+            assert force_text(resp.content) == 'OK'
 
     def test_fallback_to_normal_methods(self):
         """
         Ajax methods should fallback to normal methods by default.
         """
         test_cases = [
-            (u'get', u'get'),
-            (u'post', u'post'),
-            (u'put', u'get'),
-            (u'delete', u'get'),
+            ('get', 'get'),
+            ('post', 'post'),
+            ('put', 'get'),
+            ('delete', 'get'),
         ]
 
         for ajax_method, fallback in test_cases:
@@ -59,7 +58,7 @@ class TestAjaxResponseMixin(TestViewHelper, test.TestCase):
             m.return_value = HttpResponse()
             req = self.build_request()
             setattr(mixin, fallback, m)
-            fn = getattr(mixin, u"{0}_ajax".format(ajax_method))
+            fn = getattr(mixin, "{0}_ajax".format(ajax_method))
             ret = fn(req, 1, 2, meth=ajax_method)
             # check if appropriate method has been called
             m.assert_called_once_with(req, 1, 2, meth=ajax_method)
@@ -75,8 +74,8 @@ class TestJSONResponseMixin(TestViewHelper, test.TestCase):
 
     def assert_json_response(self, resp, status_code=200):
         self.assertEqual(status_code, resp.status_code)
-        self.assertEqual(u'application/json',
-                         resp[u'content-type'].split(u';')[0])
+        self.assertEqual('application/json',
+                         resp['content-type'].split(';')[0])
 
     def get_content(self, url):
         """
@@ -92,9 +91,9 @@ class TestJSONResponseMixin(TestViewHelper, test.TestCase):
         Tests render_json_response() method.
         """
         user = UserFactory()
-        self.client.login(username=user.username, password=u'asdf1234')
-        data = json.loads(self.get_content(u'/simple_json/'))
-        self.assertEqual({u'username': user.username}, data)
+        self.client.login(username=user.username, password='asdf1234')
+        data = json.loads(self.get_content('/simple_json/'))
+        self.assertEqual({'username': user.username}, data)
 
     def test_serialization(self):
         """
@@ -102,14 +101,14 @@ class TestJSONResponseMixin(TestViewHelper, test.TestCase):
         using django's serializer framework.
         """
         a1, a2 = [ArticleFactory() for __ in range(2)]
-        data = json.loads(self.get_content(u'/article_list_json/'))
+        data = json.loads(self.get_content('/article_list_json/'))
         self.assertIsInstance(data, list)
         self.assertEqual(2, len(data))
         titles = []
         for row in data:
             # only title has been serialized
-            self.assertEqual(1, len(row[u'fields']))
-            titles.append(row[u'fields'][u'title'])
+            self.assertEqual(1, len(row['fields']))
+            titles.append(row['fields']['title'])
 
         self.assertIn(a1.title, titles)
         self.assertIn(a2.title, titles)
@@ -128,12 +127,12 @@ class TestJSONResponseMixin(TestViewHelper, test.TestCase):
         is longer than the normal one.
         """
         user = UserFactory()
-        self.client.login(username=user.username, password=u'asfa')
-        normal_content = self.get_content(u'/simple_json/')
-        self.view_class.json_dumps_kwargs = {u'indent': 2}
-        pretty_content = self.get_content(u'/simple_json/')
-        normal_json = json.loads(u'{0}'.format(normal_content))
-        pretty_json = json.loads(u'{0}'.format(pretty_content))
+        self.client.login(username=user.username, password='asfa')
+        normal_content = self.get_content('/simple_json/')
+        self.view_class.json_dumps_kwargs = {'indent': 2}
+        pretty_content = self.get_content('/simple_json/')
+        normal_json = json.loads('{0}'.format(normal_content))
+        pretty_json = json.loads('{0}'.format(pretty_content))
         self.assertEqual(normal_json, pretty_json)
         self.assertTrue(len(pretty_content) > len(normal_content))
 
@@ -141,25 +140,25 @@ class TestJSONResponseMixin(TestViewHelper, test.TestCase):
         """
         Tests setting custom `json_encoder_class` attribute.
         """
-        data = json.loads(self.get_content(u'/simple_json_custom_encoder/'))
-        self.assertEqual({u'numbers': [1, 2, 3]}, data)
+        data = json.loads(self.get_content('/simple_json_custom_encoder/'))
+        self.assertEqual({'numbers': [1, 2, 3]}, data)
 
 
 class TestJsonRequestResponseMixin(TestViewHelper, test.TestCase):
     view_class = JsonRequestResponseView
-    request_dict = {u'status': u'operational'}
+    request_dict = {'status': 'operational'}
 
     def test_get_request_json_properly_formatted(self):
         """
         Properly formatted JSON requests should result in a JSON object
         """
-        data = json.dumps(self.request_dict).encode(u'utf-8')
+        data = json.dumps(self.request_dict).encode('utf-8')
         response = self.client.post(
-            u'/json_request/',
-            content_type=u'application/json',
+            '/json_request/',
+            content_type='application/json',
             data=data
         )
-        response_json = json.loads(response.content.decode(u'utf-8'))
+        response_json = json.loads(response.content.decode('utf-8'))
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response_json, self.request_dict)
 
@@ -168,10 +167,10 @@ class TestJsonRequestResponseMixin(TestViewHelper, test.TestCase):
         Improperly formatted JSON requests should make request_json == None
         """
         response = self.client.post(
-            u'/json_request/',
+            '/json_request/',
             data=self.request_dict
         )
-        response_json = json.loads(response.content.decode(u'utf-8'))
+        response_json = json.loads(response.content.decode('utf-8'))
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response_json, None)
 
@@ -181,10 +180,10 @@ class TestJsonRequestResponseMixin(TestViewHelper, test.TestCase):
         or None, the client should get a 400 error
         """
         response = self.client.post(
-            u'/json_bad_request/',
+            '/json_bad_request/',
             data=self.request_dict
         )
-        response_json = json.loads(response.content.decode(u'utf-8'))
+        response_json = json.loads(response.content.decode('utf-8'))
         self.assertEqual(response.status_code, 400)
         self.assertEqual(response_json, self.view_class.error_response_dict)
... 104 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-django-braces.git



More information about the Python-modules-commits mailing list