ReverseRelationAdminMixin

class django_admin_reversefields.mixins.ReverseRelationAdminMixin[source]

Bases: object

Mixin to expose reverse ForeignKey bindings on admin forms.

Add this mixin to a Django admin class and declare one or more virtual fields in reverse_relations. Each virtual field renders a form control that operates on objects of ReverseRelationConfig.model by updating its fk_field to point at the current admin instance.

The mixin ensures:
  • The virtual fields appear in fieldsets without causing Django to raise unknown-field errors during form construction.

  • Querysets are filtered per request/object via limit_choices_to and can be ordered or customised with widgets.

  • Initial selections reflect the current reverse bindings for the object under edit.

  • Permission gating (optional) can hide/disable fields at render-time and block unauthorized selections during validation.

  • On save, only authorized fields are persisted and the reverse ForeignKey(s) are synchronized to match the submitted values (unbinding anything deselected).

reverse_relations

Mapping of virtual field name to configuration. The keys here should be used inside the admin’s fieldsets like any normal field.

Type:

dict[str, ReverseRelationConfig]

reverse_relations_atomic

When True (default), all reverse relation updates performed during a form save are executed inside a single transaction.atomic() block. Within each configured field, unbinds are applied before binds to reduce transient uniqueness conflicts. Any database error will roll back the entire set of updates so no partial state is persisted. Set to False to disable transactional behavior.

Type:

bool

reverse_permissions_enabled

When True, require permission to modify reverse fields.

Type:

bool

reverse_permission_policy

Optional global policy (callable or object with has_perm) used before the default change_<model> check. Per-field config.permission still takes precedence over this.

Type:

Optional[ReversePermissionPolicy | object]

reverse_permission_mode

Behavior when user lacks permission on the reverse model for a field: - “disable”: render field disabled (read-only) and ignore posted changes - “hide”: remove field from the form

Type:

str

Usage:
>>> class MyAdmin(ReverseRelationAdminMixin, ModelAdmin):
...     reverse_relations = {
...         "site_binding": ReverseRelationConfig(
...             model=Site,
...             fk_field="meraki",
...             ordering=("displayName",),
...         )
...     }
reverse_render_uses_field_policy: bool = False

If True, the render gate consults per-field/global policies via has_reverse_change_permission() (with selection=None) instead of the base permission check. This lets per-field policies affect visibility and editability before any selection exists. Default False preserves the global/base-only render behaviour.

has_reverse_change_permission(request: HttpRequest, obj: Model | None, config: ReverseRelationConfig, selection: Any | None = None) bool[source]

Check if the user may change the reverse model for this field.

By default, requires the global change permission on the reverse model. Overrides evaluate in order of precedence: per-field policies on ReverseRelationConfig.permission, then reverse_permission_policy on the admin, followed by this fallback method. Override to enforce object-level checks or alternative permission codenames.

Parameters:
  • request (HttpRequest) – Current request (for user).

  • obj (models.Model | None) – The parent instance being edited.

  • config (ReverseRelationConfig) – Field configuration.

  • selection (Any) – Current selection, if applicable.

Returns:

True if changes are allowed.

Return type:

bool

get_reverse_relations() dict[str, ReverseRelationConfig][source]

Return the configured reverse relations for this admin.

Returns:

Mapping of virtual field names to their configuration.

Return type:

dict[str, ReverseRelationConfig]

get_fields(request, obj=None)[source]

Ensure virtual reverse field names are part of the rendered fields.

When an admin does not declare fieldsets (and does not supply fields explicitly), Django renders all form fields returned by get_fields. This override appends the virtual reverse field names so templates include them. The base get_form implementation receives the same list and our get_form override will strip virtual names before building the base form to avoid unknown-field errors.

get_form(request: HttpRequest, obj=None, **kwargs)[source]

Create a ModelForm class with injected reverse-relation fields.

This method filters out the virtual reverse field names from the fields argument (which Django constructs from fieldsets), then delegates to the parent get_form. After the base form class is created, it dynamically injects the reverse fields and wires up their querysets and initial values.

Parameters:
  • request (HttpRequest) – The current request.

  • obj (models.Model | None) – The instance being edited, if any.

  • **kwargs – Additional arguments passed to the base implementation.

Returns:

A dynamically derived form class containing the configured reverse relation fields.

Return type:

type[forms.ModelForm]

save_model(request: HttpRequest, obj, form, change)[source]

Save model and apply any deferred reverse relation updates.

This ensures reverse relations are synchronized even when the form save was called with commit=False.

Parameters:
  • request (HttpRequest) – The current request.

  • obj (models.Model) – The model instance being saved.

  • form (forms.ModelForm) – The bound form.

  • change (bool) – True if updating an existing object, False if adding.