Map Django and VUEDA Permission Names
This guide sets the Permission Mapping, places the import that applies it, and checks that the generated codenames match the ones that the server checks. Permission Name Patch describes what the import changes in Django.
Settle the mapping before the project's first migrate. Migration writes one Django: Permission row per codename, and a later mapping change does not rename those rows.
Choose a Mapping
PERMISSION_NAMES_MAPPING renames each action in a model's Django: Options.default_permissions when Django creates permission rows. VUEDA models declare create, read, update, delete, and list in default_permissions. Every other model gets list added to its defaults, unless its Meta declares its own default_permissions.
VUEDA's viewsets honour two kinds of mapping:
- The default, from core.default_settings.get_defaults. It maps
addtocreate,changetoupdate, andviewtoread. Use it for a new project. - Django's names. It maps
createtoadd,updatetochange, and bothreadandlisttoview. Use it for a database whose permission rows and group assignments already use Django's names.
Permissions lists the values for both.
The two differ in what the viewsets check. ObjectPermissions checks create_, read_, list_, update_, and delete_ codenames. Importing core.patch_django changes that for each mapping value that is add, change, or view and is not also a key. The import rewrites the matching entries of perms_map to check that name.
WARNING
Use one of these two mappings exactly. Any other mapping, such as "delete": "remove", creates permission rows that ObjectPermissions never checks. The check_permission_names_mapping system check reports it as vueda_core.E018, which stops migrate and runserver.
Under Django's names, every GET on a viewset requires the view_ codename, for both list and detail. Row-level list filtering and the choice endpoints check the same codename.
Set Mapping and Patch Import Order
To use Django's names, set
PERMISSION_NAMES_MAPPINGin your base settings module, after theget_defaultscall. For the default mapping, skip this step.Import
patch_djangoat the end of each leaf settings module, the module thatDJANGO_SETTINGS_MODULEnames. The VUEDA project template does this inconfig/settings/local.py:python# Import patch_django in the leaf settings file (not base.py) so that # PERMISSION_NAMES_MAPPING is fully resolved before the import triggers a lookup. # If you override PERMISSION_NAMES_MAPPING above, place this import after that override. from vueda.core import patch_django # noqa: F401The template has only
local.py. Add the same import to every other leaf module that you create, such as production or test settings. VUEDA does not importpatch_djangofor you.Run
python manage.py migrate.
The import reads the mapping once to decide the perms_map rewrite, so a later change to the setting does not reach ObjectPermissions. This includes Django: override_settings in tests. Codename generation, row-level list filtering, the choice endpoints, and DynamicObjectPermissions read the mapping when they run.
Check the Generated Codenames
List a model's codenames from python manage.py shell:
from django.contrib.auth.models import Permission
Permission.objects.filter(content_type__app_label="myapp", content_type__model="widget").values_list(
"codename", flat=True
)For myapp.Widget, expect these rows:
| Mapping | Codenames |
|---|---|
| Default | myapp.create_widget, myapp.read_widget, myapp.update_widget, myapp.delete_widget, myapp.list_widget |
| Django's names | myapp.add_widget, myapp.view_widget, myapp.change_widget, myapp.delete_widget |
Under Django's names, read and list both map to view, so the model has one view_widget row. Django's own models get the same set, for example auth.list_group under the default mapping.
Check Runtime Permission Checks
Give a test user one permission at a time and send each request. The table uses the default names; substitute your mapped names.
| Request | Required permission | Result |
|---|---|---|
GET list | myapp.list_widget | 200 if held, 403 if not |
GET detail | myapp.read_widget | 200 if held, 403 if not |
POST | myapp.create_widget | 201 if held, 403 if not |
PUT or PATCH | myapp.update_widget | 200 if held, 403 if not |
DELETE | myapp.delete_widget | 204 if held, 403 if not |
| Field choices | myapp.read_widget, plus list_ on the related model for a relation | 200 if all held, 403 if any missing |
| Filter choices | myapp.read_widget, plus list_ on the related model for a queryset filter | 200 if all held, 403 if any missing |
| Workflow state history | myapp.read_widget | 200 if held, 403 if not |
| Workflow object state | myapp.read_widget | 200 if held, 403 if not |
Under Django's names, the GET list and GET detail rows both require myapp.view_widget. VUEDA's own tests use the default mapping, so add these checks to your project's tests if you use Django's names.
Troubleshooting
Django and third-party models refuse every request. Their rows use add_, change_, and view_, and they have no list_ row. The leaf settings module in use does not import patch_django. VUEDA models are unaffected, because they declare CRUD action names. Check the settings module that each entry point uses: the server, the test runner, and manage.py.
manage.py check reports vueda_core.E018. The mapping is neither the default nor the full mapping to Django's names. Set it to one of the two in Choose a Mapping, then move group assignments from the old codenames to the new rows.
List access cannot be granted without detail access. A view mapping makes list and detail require the same codename.
A mapping change has no effect on existing grants. The next migrate adds rows under the new names. The old rows, the groups that hold them, and any Group Permission Migration that names them keep the old codenames. Move those assignments to the new rows yourself.
Project code checks the wrong codename. A literal codename, such as user.has_perm("myapp.read_widget"), does not follow the mapping. Update these strings when you change the mapping.