ReverseRelationConfig¶
- class django_admin_reversefields.mixins.ReverseRelationConfig(model: type[Model], fk_field: str, label: str | None = None, help_text: str | None = None, required: bool = False, multiple: bool = False, limit_choices_to: Callable[[QuerySet, Any, HttpRequest], QuerySet] | dict[str, Any] | None = None, widget: Widget | type[Widget] | None = None, ordering: Iterable[str] | None = None, clean: Callable[[Model, Any, HttpRequest], None] | None = None, permission: ReversePermissionPolicy | Callable[[HttpRequest, Model | None, ReverseRelationConfig, Any | None], bool] | object | None = None, permission_denied_message: str | None = None, bulk: bool = False)[source]¶
Bases:
objectConfiguration for a virtual reverse-relation admin field.
This dataclass describes how a virtual field should be rendered on an admin form to manage a reverse ForeignKey relationship. The virtual field does not exist on the model; instead, it controls one or more rows on the reverse-side model that hold a ForeignKey pointing back to the current object.
- model¶
The reverse-side Django model that holds the ForeignKey.
- Type:
type[models.Model]
- fk_field¶
The name of the ForeignKey field on the reverse-side model (the one specified in
model) that points back to the current admin object.- Type:
str
- label¶
Optional human-friendly label for the form field. If omitted, a label is derived from
model._meta.verbose_name(or plural whenmultipleis True).- Type:
str | None
- help_text¶
Optional help text displayed with the form field.
- Type:
str | None
- required¶
Whether a selection is required. If True, the form will enforce that at least one value is selected (for multi) or a value is present (for single).
- Type:
bool
- multiple¶
If True, a multi-select is rendered and the resulting set of objects on the reverse side will be synchronized on save. If False, a single-select is rendered and only one object may point to the current instance (others will be unbound on save).
- Type:
bool
- limit_choices_to¶
Either a callable
(queryset, instance, request) -> querysetthat can apply dynamic, per-request filtering (recommended), or a dict used asqueryset.filter(**dict)for static filtering. Common usage: include only unbound objects, plus those already bound to the current instance.- Type:
Callable | dict | None
- widget¶
Optional widget instance or class to use for rendering. Defaults to Django’s
forms.Selectfor single-select andFilteredSelectMultiplefor multi-select. You can supply Unfold, DAL, or other custom widgets here.- Type:
forms.Widget | type[forms.Widget] | None
- ordering¶
Optional ordering to apply to the limited queryset (e.g.,
("displayName",)).- Type:
Iterable[str] | None
- clean¶
Optional per-field validation hook. When provided, it is invoked from the derived form’s
clean()with(instance, selection, request). Raiseforms.ValidationErrorto attach an error to this field and block save; returnNonefor success.- Type:
Callable[[models.Model, Any, HttpRequest], None] | None
- permission¶
Optional per-field permission policy controlling whether the user may modify this virtual field. Supported values include:
A callable
(request, obj, config, selection) -> boolAn object with
has_perm(request, obj, config, selection) -> boolAn object implementing
__call__followingReversePermissionPolicy
Policy objects may expose
permission_denied_messagefor UI feedback during validation.- Type:
ReversePermissionPolicy | PermissionCallable | object | None
- permission_denied_message¶
Optional custom error message attached to the field when a selection is denied by permission checks during form validation.
- Type:
str | None
- bulk¶
When True, use Django’s .update() method for bind/unbind operations instead of individual model saves. This bypasses model signals (pre_save, post_save, etc.) but provides better performance for large datasets. Defaults to False for backward compatibility.
- Type:
bool
Example
>>> ReverseRelationConfig( ... model=Site, ... fk_field="meraki", ... label="Site", ... multiple=False, ... ordering=("displayName",), ... limit_choices_to=lambda qs, instance, request: qs.filter(meraki__isnull=True) ... )