• The changes are drastic and backwards incompatible:
    • First of all, I got rid off Django ORM-like syntax for defining permissions and replaced it with a pure and simple Python. Easier to follow (and debug).
    • The code was largely refactored, simplified and optimized (mostly thanks to the above).
    • Focused on Python 3.6+, the future is already here.
    • Removed if_false/if_true overrides.


  • Updated package to work with Django 1.1x and Python 3.5+.


  • Adding redirects inside of a wrapping P() is now possible, e.g.
class TestView(PermissionsTemplateView):

    permissions = Permissions(
        P(user__is_authenticated=True) &
            P(user__is_superuser=True) | P(object__owner=Cmp('user')), if_false=AccessDeniedView.as_view()


  • Minor maintenance release. No changes affecting current installations.


  • Minor maintenance release. No changes affecting current installations.
  • Changed policy on compatibility. Each release is now guaranteed to support:
    • latest stable release and next upcoming of Django;
    • latest Python 2.x and 3.x versions;
    • latest stable versions of interoperable packages (currently django-debug-toolbar and django-tastypie)


  • Minor maintenance release. No changes affecting current installations.


  • On a view level permissions_class is now permissions.
  • Renamed get_permissions to get_rules.
  • Renamed permissions to rules.
  • Renamed check_permissions to check.
  • Other internal API changes.
  • Object passed to permissions must be an instance.
  • Added example project at
  • Removed PERMISSIONSX_DEBUG setting.
  • Renamed PermissionsDebugPanel to PermissionsPanel (following django-debug-toolbar).


  • Bugfix release. Merging permissions with a Permissions instance with no rules defined was raising TypeError exception.


  • Removed set_request_context() method from Permissions. This was adding unjustified complexity. Instead, inheritance and super() calls can be used.
  • Added new operator: Cmp(). This allows to compare permission rules to request object even if they are not currently available in the method scope. Also, this prevents exceptions from non-existing relations (e.g. while company can be null).
  • Simplification. Removed dependency on Django patches or middleware tricks. Now if a user is anonymous and permissions are checked, and they fail on specific attributes of the User instance (e.g. get_profile()), user will be denied access for that specific rule by default.
  • Updated Django Debug Toolbar integration.
  • Added support for passing permission rules to classes having permissions already defined. This will cause all rules to be merged using AND (&). For example, following is now possible:


class ContentEditablePermissions(Permissions):

    def get_permissions(self, request, **kwargs):
            request.content = Content.objects.get(slug=kwargs.get('slug'))
        except Content.DoesNotExist:
            request.content = None
        return P(user__is_author_of=Arg('content')) | P(content__publisher=Cmp('user.publisher'))


class ContentUpdateView(DjangoViewMixin, UpdateView):

    model = Content
    template_name = 'content/content_edit.html'
    form_class = ContentCreateUpdateForm
    permissions_class = ContentEditablePermissions(

So the final result would be:

request.content.can_change_price() & (request.user.is_author_of(request.content) | (request.content.publisher == request.user.publisher))


  • Fixed Django debug toolbar panel.
  • Removed caching (explanation).


  • Added in-memory caching (settings.PERMISSIONSX_CACHING).
  • Added tests for Django Views, settings and overrides.
  • Changed the way overrides work. Few things got simplified by the way. Now it is possible to use multiple overrides attached to P objects, not the top-level Permissions.


  • Added support over overriding response behavior on a permission level.
  • One-liners for defining permissions.
  • Arg allows passing request object to permission checking function.
  • Package django-classy-tags is no longer a requirement.
  • Added Sphinx documentation with extended examples.


  • New syntax possible for retrieving related objects, e.g. P(user__get_profile__related_object__is_something=True).


  • Added support for custom response classes (e.g. for changing redirect URL, adding custom user message).
  • Added tests for checking permissions.
  • Minor fixes and improvements.


  • Added support for Django templates, including per-object checks.
  • Renamed class-level permissions to permissions_class.
  • Dropped support for simple permissions defining for the benefit of greater flexibility.
  • Renaming and refactoring, again. Good stuff: managed to get rid of middleware and a class. Things got largely simplified in general.
  • Requirement: django-classy-tags.


  • This version is backward incompatible.
  • Changed syntax to follow QuerySet filtering convention.
  • Sadly, tests are gone. Need to write new ones, what will not happen until 1.0.0 release.
  • Example project’s gone. Will be back at a different URL.
  • New setting was added: PERMISSIONSX_LOGOUT_IF_DENIED.