Skip to content

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 add to create, change to update, and view to read. Use it for a new project.
  • Django's names. It maps create to add, update to change, and both read and list to view. 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 ​

  1. To use Django's names, set PERMISSION_NAMES_MAPPING in your base settings module, after the get_defaults call. For the default mapping, skip this step.

  2. Import patch_django at the end of each leaf settings module, the module that DJANGO_SETTINGS_MODULE names. The VUEDA project template does this in config/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: F401

    The 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 import patch_django for you.

  3. 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:

python
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:

MappingCodenames
Defaultmyapp.create_widget, myapp.read_widget, myapp.update_widget, myapp.delete_widget, myapp.list_widget
Django's namesmyapp.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.

RequestRequired permissionResult
GET listmyapp.list_widget200 if held, 403 if not
GET detailmyapp.read_widget200 if held, 403 if not
POSTmyapp.create_widget201 if held, 403 if not
PUT or PATCHmyapp.update_widget200 if held, 403 if not
DELETEmyapp.delete_widget204 if held, 403 if not
Field choicesmyapp.read_widget, plus list_ on the related model for a relation200 if all held, 403 if any missing
Filter choicesmyapp.read_widget, plus list_ on the related model for a queryset filter200 if all held, 403 if any missing
Workflow state historymyapp.read_widget200 if held, 403 if not
Workflow object statemyapp.read_widget200 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.