Skip to content

8. Policies

Everything so far was a permission and a scope, and needed no code to decide. One rule of the shop is not about who is asking, but about the order: nobody edits it once it is delivered. That is a policy:

"""
A rule no permission can say: nobody edits an order once it is delivered.

Everything else - view, create, approve, print an invoice - is decided by the
permission and its scope, with no code.
"""

from clearance.policies import BasePolicy
from clearance.registry import registry
from shop.models import Order


class OrderPolicy(BasePolicy):
    model = Order
    codename = 'order'

    def can_edit(self, user, obj=None):
        if not self.has_within_scope(user, 'edit', obj):
            return False
        return obj is None or obj.status != Order.Status.DELIVERED


order_access = registry.register(OrderPolicy)
  • A model with no policy is asked through ModelPolicy: its permission, within its scope. A policy is written only for a rule no permission can say.
  • A hook is can_<action>. has_within_scope is the permission and the scope - what the model would answer with no policy - and the hook adds its own rule after it.
  • obj=None is the question asked of a list, before any row: "may they edit orders at all?"

Register it where it is imported at start-up - the shop imports it in its app's ready():

from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _


class ShopConfig(AppConfig):
    name = 'shop'
    verbose_name = _('Shop')
    default_auto_field = 'django.db.models.BigAutoField'

    def ready(self) -> None:
        from shop import policies  # noqa: F401 - registers the order policy

Asking it

Every door asks the policy - the admin site, Django's views and the API - and so can a project:

from shop.policies import order_access

order_access.policy.allows(user, 'edit', order)
order_access.policy.authorize(user, 'edit', order)    # PermissionDenied if not

south_manager holds order_edit in their branch, and the rug is South's - but it is delivered, so they may not edit it, in every door.