Commit 1f2c4b60 authored by Michael Fladischer's avatar Michael Fladischer

New upstream version 2.0.1

parent 7a2f6d38
Changelog
=========
## v2.0.1 - 2018/12/07
- Fixed issue with using ``rules`` in ``CreateView`` CBV
## v2.0.0 - 2018/07/22
- Removed support for Python 2.6 and 3.3
......
django-rules
^^^^^^^^^^^^
rules
^^^^^
``rules`` is a tiny but powerful app providing object-level permissions to
Django, without requiring a database. At its core, it is a generic framework
......
......@@ -4,6 +4,6 @@ from .predicates import (Predicate, predicate, always_true, always_false,
always_allow, always_deny, is_authenticated,
is_superuser, is_staff, is_active, is_group_member)
VERSION = (2, 0, 0, 'final', 1)
VERSION = (2, 0, 1, 'final', 1)
default_app_config = 'rules.apps.RulesConfig'
......@@ -13,6 +13,8 @@ from django.utils.encoding import force_text
# These are made available for convenience, as well as for use in Django
# versions before 1.9. For usage help see Django's docs for 1.9 or later.
from django.views.generic.edit import BaseCreateView
LoginRequiredMixin = mixins.LoginRequiredMixin
UserPassesTestMixin = mixins.UserPassesTestMixin
......@@ -34,11 +36,12 @@ class PermissionRequiredMixin(mixins.PermissionRequiredMixin):
``SingleObjectMixin``. Returns None if there's no ``get_object``
method.
"""
if hasattr(self, 'get_object') and callable(self.get_object):
# Requires SingleObjectMixin or equivalent ``get_object`` method
return self.get_object()
else: # pragma: no cover
return None
if not isinstance(self, BaseCreateView):
# We do NOT want to call get_object in a BaseCreateView, see issue #85
if hasattr(self, 'get_object') and callable(self.get_object):
# Requires SingleObjectMixin or equivalent ``get_object`` method
return self.get_object()
return None
def has_permission(self):
obj = self.get_permission_object()
......
......@@ -12,14 +12,18 @@ def is_book_author(user, book):
return book.author == user
is_editor = rules.is_group_member('editors')
@rules.predicate
def is_boss(user):
return user.is_superuser
is_editor = rules.is_group_member('editors')
# Rules
rules.add_rule('change_book', is_book_author | is_editor)
rules.add_rule('delete_book', is_book_author)
rules.add_rule('create_book', is_boss)
# Permissions
......
......@@ -2,9 +2,9 @@ from django.conf.urls import url
from django.contrib import admin
from .views import (change_book, delete_book,
view_that_raises, view_with_object, view_with_permission_list,
BookUpdateView, BookDeleteView, ViewThatRaises, ViewWithPermissionList,
BookUpdateErrorView)
view_that_raises, view_with_object, view_with_permission_list,
BookUpdateView, BookDeleteView, ViewThatRaises, ViewWithPermissionList,
BookUpdateErrorView, BookCreateView)
admin.autodiscover()
......@@ -19,6 +19,7 @@ urlpatterns = [
url(r'^(?P<book_id>\d+)/list/$', view_with_permission_list, name='view_with_permission_list'),
# Class-based views
url(r'^cbv/create/$', BookCreateView.as_view(), name='cbv.create_book'),
url(r'^cbv/(?P<book_id>\d+)/change/$', BookUpdateView.as_view(), name='cbv.change_book'),
url(r'^cbv/(?P<book_id>\d+)/delete/$', BookDeleteView.as_view(), name='cbv.delete_book'),
url(r'^cbv/(?P<book_id>\d+)/raise/$', ViewThatRaises.as_view(), name='cbv.view_that_raises'),
......
from __future__ import absolute_import
from django.http import HttpResponse
from django.views.generic.edit import UpdateView, DeleteView
from django.views.generic.edit import UpdateView, DeleteView, CreateView
from rules.contrib.views import permission_required, objectgetter
from rules.contrib.views import LoginRequiredMixin, PermissionRequiredMixin
......@@ -24,6 +24,13 @@ def change_book(request, book_id):
return HttpResponse('OK')
class BookCreateView(LoginRequiredMixin, PermissionRequiredMixin, BookMixin, CreateView):
fields = ['title']
template_name = 'empty.html'
permission_required = 'testapp.create_book'
class BookUpdateView(LoginRequiredMixin, PermissionRequiredMixin, BookMixin, UpdateView):
fields = ['title']
template_name = 'empty.html'
......
......@@ -53,6 +53,11 @@ class FBVDecoratorTests(TestData, TestCase):
self.assertEqual(response.status_code, 200)
self.assertEqual(force_str(response.content), 'OK')
# Martin can *not* create a book
self.assertTrue(self.client.login(username='martin', password='secr3t'))
response = self.client.get(reverse('cbv.create_book'))
self.assertEqual(response.status_code, 302)
# Martin can *not* delete Adrian's book and is redirected to login
self.assertTrue(self.client.login(username='martin', password='secr3t'))
response = self.client.get(reverse('delete_book', args=(1,)))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment