Rendering & Visibility¶
This guide explains how the mixin gets your virtual fields onto the page, how to control whether they show up, and when they are editable vs. read-only. It ties the lifecycle together so you can reason about layout and permissions in one place.
How virtual fields appear¶
get_fields: The mixin appends the virtual names declared inreverse_relationsto the list of fields returned byModelAdmin.get_fieldsso templates know about them.get_form: It strips those names from the basefieldspassed to the form factory (to avoid unknown-field errors), then injects realModelChoiceField/ModelMultipleChoiceFieldinstances with the configured label, help text, widget, queryset, and initial selection.
Layout rules (fields vs. fieldsets)¶
If you declare
fieldsetsorfields, you must include the virtual names there (e.g."department_binding") or the admin template will not render them.If neither is declared, Django renders all form fields by default and the injected virtual fields appear automatically.
If you override
get_fieldswithout callingsuper(), or you return a hard-codedfieldslist that omits the virtual names, the form will still contain the injected fields but the template will not render them.
Visibility vs. editability¶
When reverse_permissions_enabled
is True the mixin runs a render gate for each virtual field:
Mode is controlled by
reverse_permission_mode:"hide"removes the field from the form (no input is rendered)."disable"keeps it visible but setsdisabled=Trueand relaxesrequiredto avoid spurious “This field is required.” errors.
By default, the render gate consults a base/global permission (roughly
change_<reverse_model>). To let per-field/global policies decide visibility up front, setreverse_render_uses_field_policyto True. In that modehas_reverse_change_permission()is called withselection=None.
Validation and persistence nuances¶
Validation errors for permission denials are attached only when a custom policy (per-field or global) participates. Base-only denials rely on the UI gating above.
Hidden/disabled fields are ignored during save. The mixin filters the payload of reverse fields during
save()so crafted POSTs cannot change a hidden or disabled field.
Troubleshooting¶
Field does not render
Ensure its virtual name appears in
fieldsetsorfields(or do not declare either), and avoid overridingget_fieldswithout callingsuper().
Field renders but is read-only
Check
reverse_permissions_enabled+reverse_permission_mode, and whetherreverse_render_uses_field_policyis True with a policy that denies access for the current user.
See also
Permissions — Modes (hide/disable), render-time policies, message precedence.
Querysets & Widgets — Limiting choices and customizing widgets.
Recipes — End-to-end examples.
Core Concepts — Lifecycle summary.