{"id":37068122,"url":"https://github.com/antonagestam/django-access-tools","last_synced_at":"2026-01-14T08:00:34.523Z","repository":{"id":14889302,"uuid":"17612994","full_name":"antonagestam/django-access-tools","owner":"antonagestam","description":"An abstract access manager for Django","archived":false,"fork":true,"pushed_at":"2020-02-02T12:52:12.000Z","size":52,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-05T20:02:48.817Z","etag":null,"topics":["access-control","authorization","django","python"],"latest_commit_sha":null,"homepage":"http://antonagestam.github.io/django-access-tools/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"FundedByMe/django-access-manager","license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/antonagestam.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-03-11T00:08:10.000Z","updated_at":"2020-02-02T12:52:11.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/antonagestam/django-access-tools","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/antonagestam/django-access-tools","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonagestam%2Fdjango-access-tools","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonagestam%2Fdjango-access-tools/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonagestam%2Fdjango-access-tools/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonagestam%2Fdjango-access-tools/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/antonagestam","download_url":"https://codeload.github.com/antonagestam/django-access-tools/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antonagestam%2Fdjango-access-tools/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28413527,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T05:26:33.345Z","status":"ssl_error","status_checked_at":"2026-01-14T05:21:57.251Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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-control","authorization","django","python"],"created_at":"2026-01-14T08:00:20.075Z","updated_at":"2026-01-14T08:00:34.351Z","avatar_url":"https://github.com/antonagestam.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Django Access Tools\n\n[![Build Status](https://travis-ci.org/antonagestam/django-access-tools.svg?branch=master)](https://travis-ci.org/antonagestam/django-access-tools)\n[![Coverage Status](https://coveralls.io/repos/antonagestam/django-access-tools/badge.svg?service=github)](https://coveralls.io/github/antonagestam/django-access-tools?branch=coverage)\n\nA tidy and extendable way of defining access requirements for views. Because\nmixins and decorators gets messy.\n\n## Installation\n\nInstall using pip:\n\n```\npip install django-access-tools\n```\n\nOr latest version in repo:\n\n```\npip install -e git+https://github.com/antonagestam/django-access-tools/#egg=access\n```\n\n## Usage\n\n### Requirements\n\nAccess requirements are specified by extending the `Requirement` class. The\n`is_fulfilled` method is what defines your logic of when the requirement is\nfulfilled. By overriding `not_fulfilled` you specify what should happen if the\nrequirement is not fulfilled. For example this simple `LoggedIn` requirement:\n\n```python\nfrom django.http import Http404\nfrom access.requirements import Requirement\n\n\nclass LoggedIn(Requirement):\n    def is_fulfilled(self):\n        return self.request.user.is_authenticated()\n    \n    def not_fulfilled(self):\n        return Http404()\n```\n\n__`Requirement.request`:__ Request object. Gets set by `Requirement.setup`.\n\n__`Requirement.args`:__ Request arguments passed to the view. Gets set by\n`Requirement.setup`.\n\n__`Requirement.kwargs`:__ Request keyword arguments passed to the view. Gets set\nby `Requirement.setup`.\n\n\n### Views\n\nAccess requirements for a view will be evaluated in the order they're specified.\nFor example `access_requirements = [LoggedIn, Active]` will have this chain of\nevents before the view is executed:\n\n- Check if `LoggedIn.is_fulfilled()` is `True`.\n- If not, make the view return `LoggedIn.not_fulfilled()` and stop.\n- Otherwise, check if `Active.is_fulfilled()` is `True`\n- If not, make the view return `Active.not_fulfilled()` and stop.\n- Otherwise continue to execute the view as normal.\n\n#### Class-based Views\n\nExtend your views with `ManagedAccessViewMixin` and specify\n`access_requirements`:\n\n```python\nfrom django.views.generic import TemplateView\nfrom access.views import ManagedAccessViewMixin\nfrom access.requirements import Active, LoggedIn\n\n\nclass MyView(ManagedAccessViewMixin, TemplateView):\n    access_requirements = [LoggedIn, Active]\n    template = 'index.html'\n```\n\n#### Functional Views\n\nFor functional views, use `Requirement.as_decorator`.\n\n```python\nfrom access.requirements import LoggedIn\n\n@LoggedIn.decorator\ndef my_view(request):\n    return \"Hello world\"\n```\n\nWhen combining many requirements for a functional view, it's recommended to use\n`access_requirements`. It returns a decorator and takes requirements as\npositional arguments.\n\n```python\nfrom access.decorators import access_requirements\nfrom access.requirements import Active, LoggedIn\n\n@access_requirements(LoggedIn, Active)\ndef my_view(request):\n    return \"Hello world\"\n```\n\n### Built-in Requirements\n\n__`PageNotFoundRequirement(Requirement)`:__ Raises `Http404()` if unfulfilled.\n\n__`Staff(PageNotFoundRequirement)`:__ Raises `Http404()` if user is not staff.\n\n__`SuperUser(PageNotFoundRequirement)`:__ Raises `Http404()` if user is not\nsuperuser.\n\n__`Active(PageNotFoundRequirement)`:__ Raises `Http404()` if user is not active.\n\n__`RedirectRequirement(Requirement)`:__ Returns `Http307(self.get_url())` if not\nfulfilled. Specify `url_name` or override `get_url` to set URL to redirect to.\nAppends the current URL as ?next=current_url by default, set `append_next =\nFalse` to prevent this.\n\n__`LoggedIn(RedirectRequirement)`:__ Returns `Http307('login')` if user is not\nauthenticated.\n\n\n### More Advanced Usage Example\n\nLet's say you have a view where the user should only be allowed access\nif they've accepted your terms of service and confirmed their email\naddress.\n\nThis example redirects the user to different views depending on if\nthey've accepted the terms of service and confirmed their email.\n`RedirectRequirement` appends `?next={url}` to the redirect URLs\nso that those views can redirect the user back after completing the\nsteps.\n\n```python\nfrom access.requirements import RedirectRequirement\n\n\nclass ProfileFieldRequirement(RedirectRequirement):\n    profile_field_name = None\n\n    def __init__(self, *args, **kwargs):\n        self.required_field_value = kwargs.pop('required_field_value', True)\n        super(ProfileFieldRequirement, self).__init__(*args, **kwargs)\n\n    def is_fulfilled(self):\n        if self.profile_field_name is None:\n            raise ImproperlyConfigured(\n                \"ProfileFieldRequirements need to specify \"\n                \"`profile_field_name`.\")\n        value = getattr(self.request.user.profile, self.profile_field_name)\n        return value == self.required_field_value\n\n\nclass AcceptedTerms(ProfileFieldRequirement):\n    url_name = 'accept_tos'\n    profile_field_name = 'accepted_terms'\n\n\nclass ConfirmedEmail(ProfileFieldRequirement):\n    url_name = 'prompt_email'\n    profile_field_name = 'confirmed_email'\n\n# ... in your views.py:\n\nfrom access.views import ManagedAccessViewMixin\n\n\nclass MyView(ManagedAccessViewMixin, View):\n    access_requirements = [AcceptedTerms, ConfirmedEmail]\n    \n    # ... view code\n \n```\n\n\n## Run tests\n\nInstall test requirements:\n\n```\n$ pip install -e .[test]\n```\n\nRun tests:\n\n```\n$ make test\n```\n\n## License\n\ndjango-access-tools is licensed under The MIT License (MIT). See [LICENSE\nfile](./LICENSE) for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantonagestam%2Fdjango-access-tools","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fantonagestam%2Fdjango-access-tools","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantonagestam%2Fdjango-access-tools/lists"}