{"id":20105469,"url":"https://github.com/nnseva/django-access","last_synced_at":"2025-08-20T10:31:17.668Z","repository":{"id":49801409,"uuid":"108535976","full_name":"nnseva/django-access","owner":"nnseva","description":"Django-Access - the application introducing dynamic evaluation-based instance-level (row-level) access rights control for Django","archived":false,"fork":false,"pushed_at":"2024-01-25T13:57:17.000Z","size":149,"stargazers_count":76,"open_issues_count":7,"forks_count":5,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-04-26T23:43:20.263Z","etag":null,"topics":["access","authority","django","django-access","dynamic","evaluation","evaluation-based","guard","instance-level","permissions","rights","rights-management","row-level","runtime"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nnseva.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-27T11:07:00.000Z","updated_at":"2024-06-21T20:18:28.622Z","dependencies_parsed_at":"2024-06-21T20:18:27.535Z","dependency_job_id":"fe3580b5-b46d-4046-b2c5-9dddf7e7e41d","html_url":"https://github.com/nnseva/django-access","commit_stats":{"total_commits":103,"total_committers":2,"mean_commits":51.5,"dds":"0.22330097087378642","last_synced_commit":"b1dbd5a2df69dff6e4a289eb6a1e316e3b61ac99"},"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nnseva%2Fdjango-access","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nnseva%2Fdjango-access/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nnseva%2Fdjango-access/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nnseva%2Fdjango-access/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nnseva","download_url":"https://codeload.github.com/nnseva/django-access/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230415317,"owners_count":18222158,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["access","authority","django","django-access","dynamic","evaluation","evaluation-based","guard","instance-level","permissions","rights","rights-management","row-level","runtime"],"created_at":"2024-11-13T17:47:08.082Z","updated_at":"2024-12-19T10:08:47.143Z","avatar_url":"https://github.com/nnseva.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Tests](https://github.com/nnseva/django-access/actions/workflows/test.yml/badge.svg)](https://github.com/nnseva/django-access/actions/workflows/test.yml)\n\n# Django-Access\n\n## Installation\n\n*Stable version* from the PyPi package repository\n```bash\npip install django-access\n```\n\n*Last development version* from the GitHub source version control system\n```\npip install git+git://github.com/nnseva/django-access.git\n```\n\n## Configuration\n\nInclude the `access` application into the `INSTALLED_APPS` list, like:\n\n```python\nINSTALLED_APPS = [\n    'django.contrib.admin',\n    'django.contrib.auth',\n    ...\n    'access',\n    ...\n]\n```\nUse the following available settings to tune the access application:\n\n- `ACCESS_STRONG_DELETION_CONTROL` settings value (default backward compatible value is False) controls, whether the restriction to delete is controlled for models not having a separate (not Inline) Admin. See below the [Backward compatible deletion control](#backward-compatible-deletion-control) section.\n\n- `ACCESS_DEFAULT_PLUGIN` settings value (`\"access.plugins.DjangoAccessPlugin\"` by default) controls, what the plugin is used as a default plugin. Value is a string referring to the plugin class appropriate to import using the `import_module` call. See below the [Default Plugin](#default-plugin) section.\n\n## Introduction\n\n### Inspiration\n\nThe standard Django access control system allows controlling access \"vertically\", basing on the instance types only.\n\nSometimes the Django-controlled application may be big enough to spread the access to it among the several administrator staff. Every user then should have its own zone of control including some subset of instances of every model in the database. We can say, that in this case, we need to have some kind of per-instance, row-level \"horizontal\" access control.\n\n### Prototypes\n\nWe have some number of instance-level (row-level, row-based) access control systems for Django, such as [Django-Guardian](http://django-guardian.readthedocs.io/), or [Django-Authority](http://django-authority.readthedocs.io/en/latest/) which assume that the database always has an explicit, instantiated, sometimes direct link between the user and the accessing instance. The [Django-Guardian](http://django-guardian.readthedocs.io/) uses general-purpose relations, while [Django-Authority](http://django-authority.readthedocs.io/en/latest/) prefers some kind of common tags.\n\nSuch a way, in these packages, we should explicitly, or in the code, assign an access link for every pair of the instance and the user. It is a bit hard work in case of multiple models and lots of users.\n\nThe [Django-Rules](https://github.com/dfunckt/django-rules) package introduces an access rules model free to evaluation in runtime, similar to our package. Unfortunately the model of the rules in the package allows to apply rules only to the particular instance of the model, not to the instance set. Such a restriction makes the package principally inefficient when we should apply rules to the arbitrary set of the model instances, as for the visibility calculation.\n\n### The core package functions\n\nThe *Django-Access* package introduces a dynamic evaluation-based instance-level (row-level) access control model. It means, that you can define any custom dynamic, evaluated in the code, rules to control, whether the particular user can access to the particular instance. It is your choice, whether the rule is based on general-purpose relations, common tags, having a direct or indirect relation to some special objects in the database, field values, timing conditions, or anything else.\n\nThe plugin-based system allows registering any custom plugin assigning access control rules for any particular, or abstract model in your project.\n\nThe predefined set of plugin classes contains standard model-level *DjangoAccessPlugin* taking in account the former Django permission system like the Django itself does it.\n\nAny combination of plugins may be registered together for one model using predefined *CompoundPlugin*, which checks the access rules per every plugin in the registered combination.\n\nThe standalone *AccessModelAdmin*, as well as inline *AccessTabularInline* and *AccessStackedInline* model admin classes, introduced by the package, are based on the standard Django admin classes and take into account the both, model-wide and instance-level access rules registered in your project.\n\nYou can create a custom Model Admin class basing on one of model admin classes introduced by the package, for your own model, or redefine any standard Django, or third-party Model Admin, or even your own former Model Admin class in the existent project, using a special *AccessControlMixin* introduced by the package.\n\nAccess control customization for Tastypie is [already implemented](https://github.com/nnseva/django-access-tastypie). Other packages support is coming soon.\n\n## Using the *Django-Access* package in the admin\n\nIn order to use custom access rules in the admin view, you should tell admin classes to take custom access rules into account. You are doing it using modified admin classes.\n\nFor the backward compatibility purposes, if no any rules are customized, the modified admin classes use the default access rules near to former Django model-based access rules controlled by the Django *Permission* system.\n\n### Creating your own admin classes\n\nIf you are creating a new project, you can use any of *AccessModelAdmin*, *AccessTabularInline*, and *AccessStackedInline* exactly as you were using *ModelAdmin*, *TabularInline*, and *StackedInline* standard Django model admins. For example:\n\n```python\nfrom django.contrib import admin\nfrom access.admin import *\n\nfrom someapp.models import *\n\nclass ChildAdmin(AccessTabularInline):\n    model = SomeChild\n\n# Register your models here.\nclass ObjectAdmin(AccessModelAdmin):\n    inlines = [\n        ChildAdmin,\n    ]\n\nadmin.site.register(SomeObject,ObjectAdmin)\n```\n\n### Modifying existent admin classes\n\nIf you are using standard Django models with their admins, or third-party packages with their admins, you can modify existent Django admin classes using *AccessControlMixin*, and re-register the admin classes for the correspondent models. For example:\n\n```python\nfrom django.contrib import admin\nfrom django.contrib.auth import models\nfrom django.contrib.auth.admin import UserAdmin, GroupAdmin\nfrom access.admin import *\n\n# Register your models here.\nclass AccessUserAdmin(AccessControlMixin,UserAdmin):\n    pass\n\nclass AccessGroupAdmin(AccessControlMixin,GroupAdmin):\n    pass\n\nadmin.site.unregister(models.User)\nadmin.site.register(models.User,AccessUserAdmin)\nadmin.site.unregister(models.Group)\nadmin.site.register(models.Group,AccessGroupAdmin)\n```\n\nSometimes you need to tune external admin classes to restrict access to some fields etc. You always can do it using\nsuch a technique.\n\n## Using the *Django-Access* package for your purposes\n\nYou are free to check access to models and instances exactly the same as the *Django-Access* application does it.\n\nUse lightweight `AccessManager` object instances to control access to the particular model and proper call to the plugin registry.\n\nFor example:\n\n```python\n...\nfrom access.managers import AccessManager\n...\n\n...\n    def has_add_permission(self, request):\n        r = AccessManager(self.model).appendable(request) is not False\n        return r\n\n    def has_delete_permission(self, request, obj=None):\n        manager = AccessManager(self.model)\n        if manager.check_deleteable(self.model, request) is False:\n            return False\n        if obj:\n            return bool(manager.apply_deleteable(obj.__class__.objects.filter(id=obj.id), request))\n        return True\n\n    def has_view_permission(self, request, obj=None):\n        manager = AccessManager(self.model)\n        if manager.check_visible(self.model, request) is False:\n            return False\n        if obj:\n            return bool(manager.apply_visible(obj.__class__.objects.filter(id=obj.id), request))\n        return True\n```\n\nDon't forget to check the return value of the `check_` method against False value explicitly:\n\n```python\n        if not manager.check_visible(self.model, request): # BAD\n            ...\n        if manager.check_visible(self.model, request) is False: # GOOD\n            ...\n```\n\nTo check access to the particular instances or their groups determined\nby the filtering expression, the simplified code returning querysets may be used instead, f.e.:\n\n```python\n...\nfrom access.managers import AccessManager\n...\n\n...\n    def has_delete_permission(self, request, obj):\n        manager = AccessManager(self.model)\n        return manager.deleteable(request).filter(id=obj.id)\n\n    def has_view_permission(self, request, obj):\n        manager = AccessManager(self.model)\n        return manager.visible(request).filter(id=obj.id)\n```\n\n### Permissions which can be checked by the manager\n\nThe manager provides a set of methods controlling access to the model class as a whole, as well as to separate instances of the model.\n\nAll manager methods controlling the access are having names started from `check_` and `apply_` prefixes.\n\nA method whose name is starting from the `check_` prefix controls access to the model as a whole,\nwhile method whose name is starting from the `apply_` prefix controls access to separate instances of the model.\n\nThe second part of the access control method name defines a particular type of the access.\nThe programmer customizing the access control is free to define any types of the access,\nbut the Model Admin classes of the package itself use the only following access types:\n\n- `appendable`\n- `changeable`\n- `deleteable`\n- `visible`\n\nThe `appendable` access type is used only with `check_` prefix, while others - with both prefixes.\n\n\n### Check access to the model as a whole\n\nManager method controlling access to the whole model is named using a `check_` prefix.\nThe second part of the name determines an access type to be checked.\n\nParameters of the method are the `model` - a Django Model class to be checked,\nand `request` - a Django Request object determining an access context to be controlled.\n\nThe return value of the `check_` access control method can be of two kinds.\nThe False value forbids access, while a dictionary (even empty) means access allowed.\n\nThe non-empty dictionary returned from the `check_appendable` method of the manager will\nbe used to fill the initial values for fields when constructing a new instance of the model.\nThe dictionary keys will be correspondent to instance property names.\nWhen the instance property refers to the instance set (reverse part of the foreign key) and value is iterable,\nvalues returned from the iterable will be `add`-ed to the property.\n\n### Check access to model instances\n\nManager method controlling access to separate model instances is named using an `apply_` prefix.\nThe second part of the name determines an access type to be checked.\n\nParameters of the method are the `queryset` - a Django `QuerySet` object to be filtered,\nand `request` - a Django Request object determining an access context to be controlled.\n\nThe return value of the `apply_` access control method is a `QuerySet` filtered\nto only allowed instances accordingly to the granted access.\n\n### Standard set of the manager methods\n\nThe manager provides the following standard set of methods to check access:\n\n- `check_appendable(model, request)`\n- `check_changeable(model, request)`\n- `check_deleteable(model, request)`\n- `check_visible(model, request)`\n- `apply_changeable(queryset, request)`\n- `apply_deleteable(queryset, request)`\n- `apply_visible(queryset, request)`\n\n### Check permissions for the custom access type\n\nThe programmer can declare any new access type using access plugins (see below). The manager allows to check these types\nusing the same name convention. For example, if some plugin declares `pushable` access type on the instance level,\nyou can always call `manager.apply_pushable(queryset, request)` method to get the queryset returning only `pushable` instances.\nFor those models where this access type is not determined, the method just returns the original queryset.\n\nYou can call `apply_` or `check_` method of the manager for the access type even if it was not yet declared. The\nmanager just returns values meaning no restrictions in this case. It allows to create access\ncontrol rules and develop other types of your project independently.\n\n## Customising access rules using plugins\n\nWhen the admin is ready to use custom access rules, you can define your own access rules using predefined or your own plugins.\n\n### Registering a plugin\n\nThe *Django-Access* package uses a global access control plugin registry. Every plugin instance is registered in the registry using the model class where it should be applied as a registry key. Registering plugins can be made using `AccessManager.register_plugin`, or `AccessManager.register_plugins` static methods. The `AccessManager.register_plugin` method takes a model class, and plugin instance as two parameters, while `AccessManager.register_plugins` takes one dictionary parameter with model classes as keys, and plugin instances as values.\n\nYou can register plugins for any model classes, either standard, or from third-party packages, or your own. *Note* that you can register the only one plugin instance for the model. Registering another plugin instance for the same model unregisters the previous one. In order to combine several plugins, you can use a provided *CompoundPlugin* as described below.\n\nYou can register a plugin for any `Model` class, even an *abstract* one. This `Model` and *all its successors* (except those for which the own plugin is registered) will be controlled by this plugin instance.\n\nWe recommend register plugins in the models.py module of the separate django application without its own models. Put this application after the all others in the `INSTALLED_APPS` section of the settings module.\n\nFor example:\n\n```python\nfrom __future__ import unicode_literals\n\nfrom django.db import models\nfrom django.db.models.query import Q\nfrom django.contrib.auth.models import User, Group, Permission\n\nfrom access.plugins import CompoundPlugin, ApplyAblePlugin, CheckAblePlugin, DjangoAccessPlugin\nfrom access.managers import AccessManager\n\nfrom someapp.models import SomeObject, SomeChild\n\n\nAccessManager.register_plugins({\n    Permission:ApplyAblePlugin(visible=lambda queryset, request: queryset.filter(\n            Q(user=request.user) |\n            Q(group__in=request.user.groups.all())\n        )),\n    User:CompoundPlugin(\n        DjangoAccessPlugin(),\n        ApplyAblePlugin(\n            changeable=lambda queryset, request: queryset.filter(Q(id=request.user.id)),\n            deleteable=lambda queryset, request: queryset.filter(Q(id=request.user.id)),\n        )\n    ),\n    Group:CompoundPlugin(\n        DjangoAccessPlugin(),\n        CheckAblePlugin(\n            appendable=lambda model, request: {'user_set':[request.user]}\n        ),\n        ApplyAblePlugin(\n            visible=lambda queryset, request: queryset.filter(user=request.user),\n            changeable=lambda queryset, request: queryset.filter(user=request.user),\n            deleteable=lambda queryset, request: queryset.filter(user=request.user),\n        ),\n    )\n})\n\nAccessManager.register_plugins({\n    SomeObject: CompoundPlugin(\n        DjangoAccessPlugin(),\n        ApplyAblePlugin(\n            visible=lambda queryset, request: queryset.filter(Q(editor_group__in=request.user.groups.all())|Q(viewer_groups__in=request.user.groups.all())),\n            changeable=lambda queryset, request: queryset.filter(Q(editor_group__in=request.user.groups.all())),\n            #deleteable=lambda queryset, request: queryset.filter(Q(editor_group__in=request.user.groups.all())).exclude(Q(children__is_archived=False)),\n            deleteable=lambda queryset, request: queryset.filter(Q(editor_group__in=request.user.groups.all())),\n        )\n    ),\n    SomeChild: CompoundPlugin(\n        DjangoAccessPlugin(),\n        ApplyAblePlugin(\n            visible=lambda queryset, request: queryset.filter(Q(is_archived=False)\u0026(Q(parent__editor_group__in=request.user.groups.all())|Q(parent__viewer_groups__in=request.user.groups.all()))),\n            changeable=lambda queryset, request: queryset.filter(Q(parent__editor_group__in=request.user.groups.all())),\n            deleteable=lambda queryset, request: queryset.filter(Q(is_archived=True) \u0026 Q(parent__editor_group__in=request.user.groups.all())),\n        )\n    )\n})\n```\n\n```python\nINSTALLED_APPS = [\n    # Django applications\n    'django.contrib.admin',\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'django.contrib.messages',\n    'django.contrib.staticfiles',\n\n    # Access package\n    'access',\n\n    # Project own models\n    'someapp',\n\n    # Here the special application to register all access rules\n    'accessprofile',\n]\n\n```\n\n### Default plugin\n\nWhen no any plugin is found for the model, the default plugin is used instead. The static method `AccessManager.get_default_plugin` returns a just constructed default plugin instance. The constructor takes no parameters.\n\nThe `ACCESS_DEFAULT_PLUGIN` settings value determines a ***string*** referring default plugin class appropriate to import using import_module standard python function.\n\n### Plugin interface\n\nEvery plugin provides a set of methods controlling access to the model class as a whole, as well as to separate instances of the model. The *Django-Access* package controls, which methods are defined by the plugin instance and use it to check whether the particular user can have access to the particular instance by the particular way.\n\nAll plugin methods controlling the access are having names started from `check_` and `apply_` prefixes. A method whose name is starting from the `check_` prefix controls access to the model as a whole, while method whose name is starting from the `apply_` prefix controls access to separate instances of the model.\n\nThe second part of the access control method name defines a particular type of the access. The programmer is free to define any types of the access, but the Model Admin classes of the package itself use the only following access types:\n\n- `appendable`\n- `changeable`\n- `deleteable`\n- `visible`\n\nThe `appendable` access type is used only with `check_` prefix, while others - with both prefixes.\n\n### Check access to the model as a whole\n\nPlugin method controlling access to the whole model is named using a `check_` prefix. The second part of the name determines an access type to be checked.\n\nParameters of the method are the `model` - a Django Model class to be checked, and `request` - a Django Request object determining an access context to be controlled. The plugin instance is free to verify, what parts of the request access context are important and should be verified against a model. Plugins provided by the *Django-Access* package assume that the request contains a `user` property referring a current user, and user instance has an `is_superuser` flag.\n\nThe return value of the `check_` access control method can be of two kinds. The False value forbids access, while a dictionary (even empty) means access allowed. Other value types mean access allowed and may be used also, but are not compatible with CompoundPlugin.\n\nThe non-empty dictionary returned from the `check_appendable` method of the plugin will be used to fill the initial values for fields when constructing a new instance of the model. The dictionary keys will be correspondent to instance property names. When the instance property refers to the instance set (reverse part of the foreign key) and value is iterable, values returned from the iterable will be `add`-ed to the property.\n\n### Check access to model instances\n\nPlugin method controlling access to separate model instances is named using an `apply_` prefix. The second part of the name determines an access type to be checked.\n\nParameters of the method are the `queryset` - a Django `QuerySet` object to be filtered, and `request` - a Django Request object determining an access context to be controlled. The plugin instance is free to verify, what parts of the request access context are important and should be verified against model instances in the passed `QuerySet`. Plugins provided by the *Django-Access* package assume that the request contains a `user` field referring a current user, and user instance has an `is_superuser` flag.\n\nThe return value of the `apply_` access control method is a `QuerySet` filtered to only allowed instances accordingly to the granted access.\n\n### Base and custom access types\n\nThe Model Admin classes defined in the *Django-Access* package control the only four base access types:\n\n- `appendable`\n- `changeable`\n- `deleteable`\n- `visible`\n\nThe `appendable` access type is checked only against a model, using `check_` prefix, because the instance-level access of such kind has no sense.\n\nThe programmer can use any other access type in the same manner as the base ones.\n\n### Combining access rules using the *CompoundPlugin*\n\nThe *CompoundPlugin* can be used to combine access rules determined by several plugins using `and` logic. Model or instance is accessible for the user *only* if all plugins allow such an access. Just pass plugin instances into *CompoundPlugin* instance constructor.\n\nFor example:\n```python\nAccessManager.register_plugins({\n    User:CompoundPlugin(\n        DjangoAccessPlugin(),\n        ApplyAblePlugin(\n            changeable=lambda queryset, request: queryset.filter(Q(id=request.user.id)),\n            deleteable=lambda queryset, request: queryset.filter(Q(id=request.user.id)),\n        )\n    ),\n})\n```\n\nDictionaries returning by `check_` methods for the same access type of combined plugins are united and returned to the system by the *CompoundPlugin*.\n\nIf any of combined plugins `check_` method returns False, the correspondent *CompoundPlugin* method returns False, and other dictionaries returned by combined plugins `check_` method for the same access type are ignored.\n\n`QuerySet` objects are filtered sequentially by all `apply_` methods of combined plugins for the same access type and the resulting filtered `QuerySet` object is returned by the correspondent method of the *CompoundPlugin*\n\n### Creating dynamic access rules using the *CheckAblePlugin* or the *ApplyAblePlugin*\n\nThese plugins use keyword parameters of the constructor, correspondent to access types to take a callable determining the result of the correspondent access rule method. The *CheckAblePlugin* describes model-wide access rules, while *ApplyAblePlugin* determines instance-level access rules.\n\n```python\n    Group:CompoundPlugin(\n        DjangoAccessPlugin(),\n        CheckAblePlugin(\n            appendable=lambda model, request: {'user_set':[request.user]}\n        ),\n        ApplyAblePlugin(\n            visible=lambda queryset, request: queryset.filter(user=request.user),\n            changeable=lambda queryset, request: queryset.filter(user=request.user),\n            deleteable=lambda queryset, request: queryset.filter(user=request.user),\n        ),\n    )\n```\n\n### Simplified model-wide access rules using the *SimpleCheckPlugin*\n\nThis plugin simplifies checking for model-wide rules as in the *CheckAblePlugin* reducing it to return values of False and True instead of False and dictionary.\n\nFor example:\n\n```python\n        SimpleCheckPlugin(\n            appendable=lambda model, request: model._meta.app_label == \"custom\"\n        )\n```\n\n## Backward compatibility\n\n### Backward compatible access rules with *DjangoAccessPlugin*\n\nThis plugin defines access rules near to the former Django *Permission* access control system provided in the `auth` Django contributed application.\nIt checks the permissions set against the current user as it stored in the database.\n\nFor example:\n\n```python\n    Group:CompoundPlugin(\n        DjangoAccessPlugin(),\n        CheckAblePlugin(\n            appendable=lambda model, request: {'user_set':[request.user]}\n        ),\n        ApplyAblePlugin(\n            visible=lambda queryset, request: queryset.filter(user=request.user),\n            changeable=lambda queryset, request: queryset.filter(user=request.user),\n            deleteable=lambda queryset, request: queryset.filter(user=request.user),\n        ),\n    )\n```\n\n### Backward compatible deletion control\n\nThe `ACCESS_STRONG_DELETION_CONTROL` settings variable (default, backward compatible value is False) controls,\nwhether the restriction to delete is controlled for models not having a separate (not Inline) Admin.\nIf yes, the forbidden instances are included in the set of protected instances when trying to delete from the Admin.\n\n### Backward compatible authorization backend\n\nYou always can redefine third-party package admin classes as described above. It totally avoids using Django authorization\nsubsystem by the admin classes. It's enough for the most such packages.\n\nBut sometimes the third-party package can use direct calls to the `User` methods checking authorization (`has_perm` most probable).\nSuch calls are redirected to the Django authorization subsystem. For such a case use the following settings:\n\n```python\nAUTHENTICATION_BACKENDS = ['access.auth.backends.ModelBackend']\n```\n\nThis backend is inherited from the standard Django authentication backend and provides modified methods\nto **authorize** user actions accordingly to custom access rules defined in the project.\n\n***Note*** that the provided backward compatible authentication/authorization backend prevents using advanced features\nof the *Django-Access* package, such as whole request authorization context and instance filtering controlled by access rules.\nNewer use direct `User.has_perm` calls in your own code, use `AccessManager` calls instead.\n\n***Note*** that the Django authorization subsystem checks permissions for the user only, while the *Django-Access* package\nallows checking permissions in the whole request context. So, in case of using this authentication backend,\nyour custom access rules should be ready to take a fake request object as a callback parameter instead of real one. The fake\nrequest object is constructed and passed to the rule with the only `user` property compatible with a real Django request.\n\n## Compatibility issues\n\nThe package has been developed and tested against:\n\n- Python 2.7\n    - Django v.1.10\n    - Django v.1.11\n- Python 3.6\n    - Django v.1.10\n    - Django v.1.11\n    - Django v.2.0\n    - Django v.2.1\n    - Django v.2.2\n    - Django v.3.0\n- Python 3.7\n    - Django v.2.0\n    - Django v.2.1\n    - Django v.2.2\n    - Django v.3.0\n- Python 3.8\n    - Django v.2.0\n    - Django v.2.1\n    - Django v.2.2\n    - Django v.3.0\n    - Django v.3.1\n    - Django v.3.2\n    - Django v.4.0\n    - Django v.4.1\n    - Django v.4.2\n- Python 3.9\n    - Django v.2.0\n    - Django v.2.1\n    - Django v.2.2\n    - Django v.3.0\n    - Django v.3.1\n    - Django v.3.2\n    - Django v.4.0\n    - Django v.4.1\n    - Django v.4.2\n- Python 3.10\n    - Django v.3.2\n    - Django v.4.0\n    - Django v.4.1\n    - Django v.4.2\n- Python 3.11\n    - Django v.3.2\n    - Django v.4.0\n    - Django v.4.1\n    - Django v.4.2\n\nIt also can be compatible with other versions and combinations, but not obviously\n\n## Examples\n\nJust look into the *example* folder.\n\nThe example project uses `django.contrib.auth` models and also has an own application `someapp` introducing two models:\n- `SomeObject` controlled by the separate `ModelAdmin` having a foreign key to the `Group` of editors, and many-to-many relation to `Group`s of viewers\n- `SomeChild` which has a foreign key to the `SomeObject` and controlled by the `InlineAdmin`\n\nThe example is oriented to the following access scheme:\n- The superuser can anything\n- Django Permissions are applied except User\n- The other User record is accessible for reading except e-mail and password\n- The other User record is accessible for writing (except `is_superuser` flag and timestamp fields) and deleting if it is not a superuser and Django permissions granted\n- The own User record is accessible for writing (except `is_superuser` flag and timestamp fields)\n\n- Groups and Permissions are visible only for those Users who have relations to them\n- SomeObject is visible for viewers, and changeable for editors, defined by the related `Group` instances\n\nWhen the `Group` is created, the creator is included into this `Group`.\n\nCode from the example is used in this file to illustrate different details of the package. You can see the original example on the GitHub repository.\n\n## See also\n\nThe [Django-Access-Tastypie](https://github.com/nnseva/django-access-tastypie) package introduces an authorization backend for the [Django-Tastypie package](https://django-tastypie.readthedocs.io/en/latest/) to use access rules defined here.\n\nThe [Django-REST-Access](https://github.com/nnseva/django-rest-access) package introduces a permission control and filtering backend for the [Django REST Framework](https://www.django-rest-framework.org) to use access rules defined here.\n\nThe [Django-Access-Select2](https://github.com/nnseva/django-access-select2) package provides a filtering for the [Django-Select2](http://django-select2.readthedocs.io/en/latest/) package (obsoletted for now) to use access rules defined here.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnnseva%2Fdjango-access","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnnseva%2Fdjango-access","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnnseva%2Fdjango-access/lists"}