### Install django-admin-reversefields using pip or uv Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/quickstart.rst Installs the django-admin-reversefields package using either pip or uv, the fast Python package installer. ```bash python -m pip install django-admin-reversefields ``` ```bash uv pip install django-admin-reversefields ``` -------------------------------- ### Install Dependencies Source: https://github.com/tnware/django-admin-reversefields/blob/main/AGENTS.md Installs project dependencies using 'uv'. This is a command-line tool for managing Python environments and packages. ```bash uv sync ``` -------------------------------- ### Django Admin Quickstart Example Source: https://github.com/tnware/django-admin-reversefields/blob/main/README.md Demonstrates how to use ReverseRelationAdminMixin and ReverseRelationConfig in a Django admin model. It shows how to define reverse relations for single and multiple selections, including custom queryset filtering. ```python from django.contrib import admin from django.db.models import Q from django_admin_reversefields.mixins import ( ReverseRelationAdminMixin, ReverseRelationConfig, ) from .models import Company, Department, Project def unbound_or_current(qs, instance, request): if instance and instance.pk: return qs.filter(Q(company__isnull=True) | Q(company=instance)) return qs.filter(company__isnull=True) @admin.register(Company) class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { # Single-select: bind exactly one Department via its FK to Company "department_binding": ReverseRelationConfig( model=Department, fk_field="company", limit_choices_to=unbound_or_current, ), # Multi-select: manage the entire set of Projects pointing at the Company "assigned_projects": ReverseRelationConfig( model=Project, fk_field="company", multiple=True, # optional: ordering=("name",), ), } fieldsets = (("Relations", {"fields": ("department_binding", "assigned_projects")}),) ``` -------------------------------- ### Build Package Source: https://github.com/tnware/django-admin-reversefields/blob/main/AGENTS.md Builds the Python package for distribution. This command is typically used before publishing the package. ```bash uv build ``` -------------------------------- ### Minimal Django admin with two reverse bindings Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/quickstart.rst A minimal example of a Django admin class inheriting from ReverseRelationAdminMixin, exposing two reverse bindings for 'department_binding' and 'assigned_projects'. It demonstrates the structure of the reverse_relations dictionary and the use of ReverseRelationConfig. ```python from django.contrib import admin from django_admin_reversefields.mixins import ReverseRelationAdminMixin from django_admin_reversefields.config import ReverseRelationConfig class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "department_binding": ReverseRelationConfig( model=Department, fk_field="company", ), "assigned_projects": ReverseRelationConfig( model=Project, fk_field="company", ordering=("name",), ) } fieldsets = ( (None, {"fields": ("name", "department_binding", "assigned_projects")}), ) ``` -------------------------------- ### Clone and Install Django Admin Reverse Fields with uv Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/development.rst Clones the project repository, sets up a virtual environment using uv, activates it, and installs the project and its documentation dependencies. ```bash git clone https://github.com/tnware/django-admin-reversefields cd django-admin-reversefields uv venv .venv # Windows PowerShell . .\.venv\Scripts\Activate.ps1 # macOS/Linux # source .venv/bin/activate uv pip install -e . uv pip install -r docs/requirements.txt ``` -------------------------------- ### Build Documentation Source: https://github.com/tnware/django-admin-reversefields/blob/main/AGENTS.md Builds the project's documentation in HTML format using Sphinx. The '-W' flag treats warnings as errors, ensuring documentation quality. ```bash uv run sphinx-build -b html docs docs/_build/html -W ``` -------------------------------- ### Install django-admin-reversefields Source: https://github.com/tnware/django-admin-reversefields/blob/main/README.md Installs the django-admin-reversefields package using pip. This is the primary method for integrating the library into your Django project. ```bash pip install django-admin-reversefields ``` -------------------------------- ### Required imports for Django admin configuration Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/quickstart.rst Shows the necessary imports from django.contrib.admin and django_admin_reversefields.mixins to configure reverse relations in your Django admin. ```python from django.contrib import admin from django_admin_reversefields.mixins import ReverseRelationAdminMixin from django_admin_reversefields.config import ReverseRelationConfig class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "department_binding": ReverseRelationConfig( model=Department, fk_field="company", ) } ``` -------------------------------- ### Format Code with Ruff Source: https://github.com/tnware/django-admin-reversefields/blob/main/AGENTS.md Formats the code according to style guidelines using Ruff. This ensures consistent code style across the project. ```bash uv run ruff format . ``` -------------------------------- ### Lint Code with Ruff Source: https://github.com/tnware/django-admin-reversefields/blob/main/AGENTS.md Checks the code for style and potential errors using the Ruff linter. Ruff is a fast Python linter and formatter. ```bash uv run ruff check . ``` -------------------------------- ### Run Django Tests Source: https://github.com/tnware/django-admin-reversefields/blob/main/AGENTS.md Executes the project's tests using the Django test runner. This command ensures the application's functionality remains intact. ```bash uv run django-admin test ``` ```bash uv run python manage.py test ``` -------------------------------- ### Multiple Binding: Company to Projects Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Illustrates how to manage multiple reverse relationships between a Company and its Projects. This example uses the 'multiple=True' option and a custom queryset filter. ```python from django.db.models import Q def available_projects_queryset(queryset, instance, request): if instance and instance.pk: return queryset.filter(Q(company__isnull=True) | Q(company=instance)) return queryset.filter(company__isnull=True) @admin.register(Company) class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "assigned_projects": ReverseRelationConfig( model=Project, fk_field="company", multiple=True, ordering=("name",), limit_choices_to=available_projects_queryset, # Enable bulk operations for better performance with many projects # bulk=True, # Uncomment if you don't need model signals ) } fieldsets = (("Projects", {"fields": ("assigned_projects",)}),) # Rendering rules are the same as the single-binding recipe. # Ensure all updates occur as a single unit (default True) reverse_relations_atomic = True ``` -------------------------------- ### Enable bulk operations for performance Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/quickstart.rst Shows how to enable bulk operations for reverse relations by setting 'bulk=True' in ReverseRelationConfig. This improves performance for large datasets by using Django's .update() method, bypassing model signals. ```python class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "department_binding": ReverseRelationConfig( model=Department, fk_field="company", bulk=True, limit_choices_to=unbound_or_current, ), "assigned_projects": ReverseRelationConfig( model=Project, fk_field="company", multiple=True, bulk=True, ordering=("name",), ) } ``` -------------------------------- ### Django Admin Reverse Relation Configuration Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Example configuration for reverse relations in Django admin, specifying models, foreign key fields, and whether to use bulk operations for performance. ```python reverse_relations = { "department_assignments": ReverseRelationConfig( model=Department, fk_field="company", multiple=True, bulk=True, # 10-50x performance improvement ordering=("name",), ) } ``` -------------------------------- ### Django Admin Permission Logic Examples Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/permissions-guide.rst Demonstrates different ways to define custom permission logic for reverse fields in Django admin. Includes simple function-based rules, stateful policy objects, and adapter classes for legacy permission checks. ```python def only_staff(request, obj, config, selection): return getattr(request.user, "is_staff", False) ReverseRelationConfig(..., permission=only_staff) ``` ```python class OrgPolicy: def __init__(self, org_id: int): self.org_id = org_id permission_denied_message = "You lack access to this organization." def __call__(self, request, obj, config, selection): return getattr(request.user, "org_id", None) == self.org_id ReverseRelationConfig(..., permission=OrgPolicy(org_id=42)) ``` ```python class CanBindAdapter: permission_denied_message = "Not allowed to bind this item." def has_perm(self, request, obj, config, selection): # delegate to some legacy checker return legacy_can_bind(request.user, selection) ReverseRelationConfig(..., permission=CanBindAdapter()) ``` -------------------------------- ### Permissions: Adapt Legacy has_perm Helpers Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Provides an example of adapting legacy permission checking functions for use with reverse fields. It defines a class 'CanBindAdapter' that delegates permission checks to a hypothetical 'legacy_can_bind' function. ```python class CanBindAdapter: permission_denied_message = "Not allowed to bind this item." def has_perm(self, request, obj, config, selection): # delegate to some legacy checker return legacy_can_bind(request.user, selection) ``` -------------------------------- ### Django Admin: Configure Company with Reverse Relation Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst This snippet shows the basic setup of a Django admin model, CompanyAdmin, inheriting from ReverseRelationAdminMixin and configuring a reverse relation for department_binding. ```python class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_permissions_enabled = True reverse_relations = { "department_binding": ReverseRelationConfig( model=Department, fk_field="company", multiple=False, permission=CanBindAdapter(), ) } ``` -------------------------------- ### Limit choices dynamically with a callable Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/quickstart.rst Demonstrates how to limit choices for a reverse relation using a custom callable function 'unbound_or_current'. This function filters the queryset based on the current instance and request, ensuring only relevant items are displayed. ```python from django.db.models import Q def unbound_or_current(queryset, instance, request): if instance and instance.pk: return queryset.filter(Q(company__isnull=True) | Q(company=instance)) return queryset.filter(company__isnull=True) class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "department_binding": ReverseRelationConfig( model=Department, fk_field="company", limit_choices_to=unbound_or_current, ) } ``` -------------------------------- ### Example Usage of ReverseRelationAdminMixin in Django Admin Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/core-concepts.rst Demonstrates how to use ReverseRelationAdminMixin to add virtual fields for reverse relationships in a Django ModelAdmin. It shows how to include these virtual fields in 'fields' or 'fieldsets' and explains how the mixin dynamically injects and manages them. ```python from django.contrib import admin from django.db import models from django_admin_reversefields.mixins import ReverseRelationAdminMixin from django_admin_reversefields.config import ReverseRelationConfig class AuthorAdmin(admin.ModelAdmin): list_display = ('name',) class BookAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): fields = ( 'title', 'author', # Include the virtual field name here 'author_books_set' ) # Or using fieldsets: # fieldsets = ( # (None, {'fields': ('title', 'author')}), # ('Related Books', {'fields': ('author_books_set',)}), # ) reverse_relations = { 'author_books_set': ReverseRelationConfig( relation_field='author', # Optional: customize label, widget, etc. # label='Books by this Author', # widget=admin.widgets.FilteredSelectMultiple('Books', is_stacked=False) ) } admin.site.register(Author, AuthorAdmin) admin.site.register(Book, BookAdmin) ``` -------------------------------- ### Validation Hook: Forbid Unbinding Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Shows how to implement custom validation logic for reverse relations using the 'clean' parameter in ReverseRelationConfig. This example prevents unbinding a department under certain conditions. ```python from django import forms def forbid_unbind(instance, selection, request): if selection is None: raise forms.ValidationError("Cannot unbind department right now") @admin.register(Company) class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "department_binding": ReverseRelationConfig( model=Department, fk_field="company", multiple=False, clean=forbid_unbind, ) } ``` -------------------------------- ### Override Default Widgets with django-unfold Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/advanced.rst Shows how to override default Django admin select widgets with Unfold's styled versions to match a custom admin theme. This example uses UnfoldAdminSelectWidget for a basic styled select input. ```python from unfold.widgets import UnfoldAdminSelectWidget @admin.register(Company) class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "department_binding": ReverseRelationConfig( model=Department, fk_field="company", widget=UnfoldAdminSelectWidget(), ) } ``` -------------------------------- ### Build and Upload Package with Uv and Twine Source: https://github.com/tnware/django-admin-reversefields/blob/main/README.md Commands to build the Python package using `uv build` and upload it to PyPI using `Twine upload`. This is part of the release process for the project. ```bash uv build Twine upload dist/* ``` -------------------------------- ### Build and Release Django Admin Reverse Fields with uv Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/development.rst Builds the project distribution packages (source and wheel) using 'uv build' or 'python -m build', and then uploads them to the Python Package Index (PyPI) using 'twine'. ```bash uv build # or: python -m build twine upload dist/* ``` -------------------------------- ### Run Django Tests with uv Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/development.rst Executes the project's Django tests with increased verbosity (level 2) using the 'uv run' command to ensure the correct Python environment is used. ```bash uv run python manage.py test -v 2 ``` -------------------------------- ### Performance Comparison: Bulk vs. Individual Saves Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Illustrates the performance difference between individual model saves and bulk operations when managing a large number of objects in Django. Bulk operations significantly reduce database queries and bypass model signals. ```python # Example performance difference with 1000 Department objects: # Individual saves (bulk=False): # - 1000+ database queries (one per save) # - All model signals triggered # - ~2-5 seconds for large operations # Bulk operations (bulk=True): # - 2 database queries (one unbind, one bind) # - No model signals triggered # - ~0.1-0.2 seconds for same operation ``` -------------------------------- ### Django Admin: Policy Object for Permissions Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Demonstrates using a class (StaffOnlyPolicy) that implements the required callable signature and defines a permission_denied_message to control access to reverse relations. ```python class StaffOnlyPolicy: permission_denied_message = "Staff access required" def __call__(self, request, obj, config, selection): return getattr(request.user, "is_staff", False) @admin.register(Company) class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_permissions_enabled = True reverse_relations = { "department_binding": ReverseRelationConfig( model=Department, fk_field="company", multiple=False, permission=StaffOnlyPolicy(), ) } ``` -------------------------------- ### Django Admin: Global Policy for Reverse Relations Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Shows how to set a global permission policy for all reverse relations in the admin by assigning a callable to reverse_permission_policy, using staticmethod to ensure correct signature. ```python def can_bind(request, obj, config, selection): # Example: require a custom permission codename on the reverse model app = config.model._meta.app_label model = config.model._meta.model_name return request.user.has_perm(f"{app}.can_bind_{model}") class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_permissions_enabled = True reverse_permission_policy = staticmethod(can_bind) # Note: # Using ``staticmethod`` prevents Python from binding ``self`` to the callable. # The mixin expects a callable with the signature ``(request, obj, config, selection)``. ``` -------------------------------- ### Django Admin: Alternative Global Policy Assignment Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Provides an alternative method for setting a global permission policy by assigning the callable directly within the get_form method, avoiding the need for staticmethod. ```python class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_permissions_enabled = True def get_form(self, request, obj=None, **kwargs): # Assign policy on the instance; no staticmethod needed self.reverse_permission_policy = can_bind return super().get_form(request, obj, **kwargs) ``` -------------------------------- ### Custom Widget with django-autocomplete-light Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/advanced.rst Demonstrates how to provide a custom widget using django-autocomplete-light for large datasets in reverse relations. This configuration customizes the selection of departments bound to a company, allowing filtering and selection via an autocomplete interface. ```python from django.contrib import admin from dal import autocomplete from django.db.models import Q from myapp.models import Company, Department from django_admin_reversefields.mixins import ReverseRelationAdminMixin, ReverseRelationConfig def company_department_queryset(queryset, instance, request): """Allow binding to departments that are unbound or already tied to ``instance``.""" if instance and instance.pk: return queryset.filter(Q(company__isnull=True) | Q(company=instance)) return queryset.filter(company__isnull=True) class DepartmentAutocomplete(autocomplete.Select2QuerySetView): def get_queryset(self): qs = Department.objects.all() term = (self.q or "").strip() if term: qs = qs.filter(name__icontains=term) return qs.order_by("name") @admin.register(Company) class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "department_binding": ReverseRelationConfig( model=Department, fk_field="company", widget=autocomplete.ModelSelect2( url="department-autocomplete", attrs={"data-minimum-input-length": 2} ), limit_choices_to=company_department_queryset, ) } ``` -------------------------------- ### Extending ReverseRelationConfig: Dynamic Querysets with Callable Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/architecture.rst Demonstrates how to scope querysets dynamically using a callable for the 'limit_choices_to' argument in ReverseRelationConfig. This callable receives the current request and instance, enabling per-user or per-instance filtering. ```python from django.contrib import admin from django_admin_reversefields.mixins import ReverseRelationAdminMixin from django_admin_reversefields.configs import ReverseRelationConfig def my_dynamic_limit(request, instance): # Filter related objects based on the current user and instance return {"created_by": request.user.id, "related_instance_id": instance.id} class MyModelAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "scoped_relations": ReverseRelationConfig( model="app.ScopedModel", foreign_key="my_model_fk", limit_choices_to=my_dynamic_limit, ), } ``` -------------------------------- ### Single Binding: Company to Department Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Demonstrates how to set up a single reverse relationship binding between Company and Department models using ReverseRelationAdminMixin. It includes a custom queryset filter for limiting choices. ```python from django.contrib import admin from django.db.models import Q from django_admin_reversefields.mixins import ReverseRelationAdminMixin, ReverseRelationConfig def unbound_or_current(queryset, instance, request): if instance and instance.pk: return queryset.filter(Q(company__isnull=True) | Q(company=instance)) return queryset.filter(company__isnull=True) @admin.register(Company) class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "department_binding": ReverseRelationConfig( model=Department, fk_field="company", limit_choices_to=unbound_or_current, # Add bulk=True for better performance with large datasets # bulk=True, # Uncomment if you don't need model signals ) } fieldsets = (("Departments", {"fields": ("department_binding",)}),) ``` -------------------------------- ### Bulk Operations for Performance (Python) Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Enables bulk mode to optimize performance for large numbers of reverse relationships by using Django's `.update()` method. This bypasses model signals but significantly improves efficiency for bulk operations. ```python from django.contrib import admin from django.db.models import Q from django_admin_reversefields.mixins import ReverseRelationAdminMixin, ReverseRelationConfig def available_departments_queryset(queryset, instance, request): if instance and instance.pk: return queryset.filter(Q(company__isnull=True) | Q(company=instance)) return queryset.filter(company__isnull=True) @admin.register(Company) class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { # Single-select with bulk operations "primary_department": ReverseRelationConfig( model=Department, fk_field="company", bulk=True, # Use bulk operations for performance limit_choices_to=available_departments_queryset, ), # Multi-select with bulk operations - ideal for large datasets "all_projects": ReverseRelationConfig( model=Project, fk_field="company", multiple=True, bulk=True, # Bulk operations for multiple selections ordering=("name",), limit_choices_to=available_departments_queryset, ), # Mixed configuration - some bulk, some individual "critical_departments": ReverseRelationConfig( model=Department, fk_field="company", multiple=True, bulk=False, # Keep individual saves for signal processing ordering=("name",), ) } ``` -------------------------------- ### ReverseRelationAdminMixin: Handling Permissions Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/architecture.rst Shows how to enable and configure permissions for reverse relations. This includes checking permissions during form cleaning and saving to prevent unauthorized modifications. ```python from django.contrib import admin from django_admin_reversefields.mixins import ReverseRelationAdminMixin class MyModelAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "related_items": ReverseRelationConfig(model="app.Item", foreign_key="my_model_fk"), } reverse_permissions_enabled = True reverse_permission_policy = "app.permissions.MyPermissionPolicy" # The mixin's clean and save_model methods handle permission checks. ``` -------------------------------- ### Configuring Multiple Selection for Reverse Relations Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/core-concepts.rst Illustrates how to configure a reverse relation to handle multiple selections using `multiple=True` in ReverseRelationConfig. This allows managing a set of related objects. ```python from django.contrib import admin from django.db import models from django_admin_reversefields.mixins import ReverseRelationAdminMixin from django_admin_reversefields.config import ReverseRelationConfig class AuthorAdmin(admin.ModelAdmin): list_display = ('name',) class BookAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): fields = ('title', 'author', 'author_books_set') reverse_relations = { 'author_books_set': ReverseRelationConfig( relation_field='author', multiple=True # Enable multiple selections ) } admin.site.register(Author, AuthorAdmin) admin.site.register(Book, BookAdmin) ``` -------------------------------- ### Import Reverse Relation Mixins and Config in Python Source: https://github.com/tnware/django-admin-reversefields/blob/main/README.md Imports the necessary classes `ReverseRelationAdminMixin` and `ReverseRelationConfig` from the `django_admin_reversefields.mixins` module for use in Django admin configurations. ```python from django_admin_reversefields.mixins import ReverseRelationAdminMixin, ReverseRelationConfig ``` -------------------------------- ### Django Autocomplete View for AJAX Search Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Defines a Django view that integrates with django-autocomplete-light to provide AJAX-powered search functionality for model fields. It includes filtering based on user input and authentication. ```python # In forms.py or a dedicated file from dal import autocomplete from .models import Department class DepartmentAutocomplete(autocomplete.Select2QuerySetView): def get_queryset(self): # Don't forget to filter out results based on user permissions if not self.request.user.is_authenticated: return Department.objects.none() qs = Department.objects.all() if self.q: qs = qs.filter(name__icontains=self.q) return qs ``` -------------------------------- ### ReverseRelationConfig: Field-Specific Configuration Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/architecture.rst Illustrates the configuration options available for individual reverse relations using ReverseRelationConfig. This includes setting labels, widgets, queryset limiters, validation hooks, and permission policies. ```python from django_admin_reversefields.configs import ReverseRelationConfig from django.forms.widgets import TextInput my_relation_config = ReverseRelationConfig( label="Custom Label", widget=TextInput, limit_choices_to=lambda request, instance: {"user_id": request.user.id}, permission_policy=my_custom_permission_policy, ) ``` -------------------------------- ### Extending ReverseRelationConfig: Custom Permission Policies Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/architecture.rst Illustrates how to implement custom permission policies for reverse relation fields, either on a per-field basis via ReverseRelationConfig.permission_policy or globally on the admin class using reverse_permission_policy. ```python from django.contrib import admin from django_admin_reversefields.mixins import ReverseRelationAdminMixin from django_admin_reversefields.configs import ReverseRelationConfig class MyCustomPermissionPolicy: def __call__(self, request, instance, field_name, selection): # Custom logic to determine if the user has permission if not request.user.is_staff: return False return True my_policy = MyCustomPermissionPolicy() class MyModelAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "restricted_field": ReverseRelationConfig( model="app.RestrictedModel", foreign_key="my_model_fk", permission_policy=my_policy, ), } # Alternatively, for a global policy: # reverse_permission_policy = my_policy ``` -------------------------------- ### Registering Autocomplete View URL in Django Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Registers the URL for a custom Django autocomplete view, making it accessible for third-party widgets like django-autocomplete-light to use for AJAX searches. ```python # In urls.py from .forms import DepartmentAutocomplete urlpatterns = [ path( "department-autocomplete/", DepartmentAutocomplete.as_view(), name="department-autocomplete", ), ] ``` -------------------------------- ### Enable and Configure Reverse Permissions in Django Admin Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/permissions-guide.rst This snippet demonstrates how to enable reverse permissions and define custom per-field policies within a Django Admin model configuration. It sets up a mixin, enables the permission system, and specifies that render policies should use field-specific configurations. A sample policy is provided for a 'department_binding' reverse relation, restricting access based on the user's staff status. ```python class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_permissions_enabled = True reverse_render_uses_field_policy = True reverse_relations = { "department_binding": ReverseRelationConfig( model=Department, fk_field="company", multiple=False, permission=lambda request, obj, config, selection: getattr(request.user, "is_staff", False), ) } ``` -------------------------------- ### Use django-unfold's Select2Widget for Enhanced UX Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/advanced.rst Illustrates using Unfold's Select2 widget for a better user experience in the Django admin when dealing with reverse relations. This provides an enhanced autocomplete experience for selecting related data. ```python from unfold.widgets import UnfoldAdminSelect2Widget @admin.register(Company) class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "department_binding": ReverseRelationConfig( model=Department, fk_field="company", widget=UnfoldAdminSelect2Widget(), ) } ``` -------------------------------- ### Configure Reverse Permissions for Persistence Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/caveats.rst When reverse_permissions_enabled is True, users must pass a configured policy before persisting changes. Denied fields are ignored during save to prevent bypassing checks. Render-time behavior is controlled by reverse_permission_mode ('disable' or 'hide'). ```python from django_admin_reversefields.mixins import ReverseRelationAdminMixin class MyModelAdmin(ReverseRelationAdminMixin): reverse_permissions_enabled = True reverse_permission_mode = "disable" # or "hide" # To enable per-field/global policies for visibility/editability: # reverse_render_uses_field_policy = True ``` -------------------------------- ### Enabling Bulk Operations for Reverse Relation Updates Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/core-concepts.rst Demonstrates how to enable bulk operations for updating reverse relations by setting `bulk=True` in ReverseRelationConfig. This uses Django's `.update()` method for performance, bypassing model signals. ```python from django.contrib import admin from django.db import models from django_admin_reversefields.mixins import ReverseRelationAdminMixin from django_admin_reversefields.config import ReverseRelationConfig class AuthorAdmin(admin.ModelAdmin): list_display = ('name',) class BookAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): fields = ('title', 'author', 'author_books_set') reverse_relations = { 'author_books_set': ReverseRelationConfig( relation_field='author', multiple=True, bulk=True # Enable bulk operations ) } admin.site.register(Author, AuthorAdmin) admin.site.register(Book, BookAdmin) ``` -------------------------------- ### Django Admin: Callable Per-Field Policy for Permissions Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Shows how to define a callable function (e.g., only_allow_special) to act as a permission policy for a specific reverse relation field, allowing fine-grained control over selections. ```python # Callable policy per field (signature must include config) def only_allow_special(request, obj, config, selection): return getattr(selection, "name", "") == "Special" @admin.register(Company) class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_permissions_enabled = True reverse_relations = { "department_binding": ReverseRelationConfig( model=Department, fk_field="company", multiple=False, permission=only_allow_special, # Optional field override for the message; otherwise the policy # object's message (if any) or a default will be used. permission_denied_message="You do not have permission to choose this value.", ) } ``` -------------------------------- ### Optimize Limit Choices for Large Datasets Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/caveats.rst Optimize limit_choices_to callables with database filtering (e.g., .filter(), .exclude()) to improve performance with large child sets. Avoid Python iteration and consider specialized widgets for better option loading. ```python from django_admin_reversefields.mixins import ReverseRelationConfig # Example of optimizing limit_choices_to # class MyModelAdmin(ReverseRelationAdminMixin): # reverse_fields = { # "my_relation": ReverseRelationConfig( # limit_choices_to=lambda qs: qs.filter(is_active=True).select_related('related_model') # ) # } ``` -------------------------------- ### Filter Choices with Callable in Django Admin Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/querysets-and-widgets.rst Demonstrates how to use a callable for `limit_choices_to` to dynamically filter querysets based on the instance and request. This allows for complex filtering logic, such as including unbound or currently bound objects. ```python from django.db.models import Q def unbound_or_current(qs, instance, request): if instance and instance.pk: return qs.filter(Q(company__isnull=True) | Q(company=instance)) return qs.filter(company__isnull=True) ``` -------------------------------- ### Configuring Reverse Relation Field with Limit Choices To Callable Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/core-concepts.rst Shows how to define a callable for `limit_choices_to` in ReverseRelationConfig. This callable receives the queryset, instance, and request, allowing dynamic scoping of choices based on the current context. ```python from django.contrib import admin from django.db import models from django_admin_reversefields.mixins import ReverseRelationAdminMixin from django_admin_reversefields.config import ReverseRelationConfig class AuthorAdmin(admin.ModelAdmin): list_display = ('name',) def get_available_books_for_author(queryset, instance, request): # Example: Limit choices to books not already assigned to this author if instance and instance.pk: return queryset.exclude(pk__in=instance.books_set.all()) return queryset class BookAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): fields = ('title', 'author', 'author_books_set') reverse_relations = { 'author_books_set': ReverseRelationConfig( relation_field='author', limit_choices_to=get_available_books_for_author ) } admin.site.register(Author, AuthorAdmin) admin.site.register(Book, BookAdmin) ``` -------------------------------- ### ReverseRelationAdminMixin: Declarative Reverse Relation Configuration Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/architecture.rst Defines how to configure reverse relations within a Django admin class using the 'reverse_relations' attribute. This attribute maps virtual field names to ReverseRelationConfig objects, specifying how to manage reverse relationships. ```python from django.contrib import admin from django_admin_reversefields.mixins import ReverseRelationAdminMixin from django_admin_reversefields.configs import ReverseRelationConfig class MyModelAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "related_objects": ReverseRelationConfig( model="app.RelatedModel", foreign_key="my_model_fk", multiselect=True, limit_choices_to={"some_field__exact": 1}, ), } fieldsets = ( (None, {"fields": ["normal_field", "related_objects"]}), ) ``` -------------------------------- ### Render-Time Policy (Python) Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Controls the visibility or editability of a reverse relation field at render time based on request context. The `permission` policy function determines visibility (hide/disable) for non-staff users. ```python from django.contrib import admin from django_admin_reversefields.mixins import ( ReverseRelationAdminMixin, ReverseRelationConfig, ) # Hide binding from non-staff users at render time def staff_only_policy(request, obj, config, selection): return getattr(request.user, "is_staff", False) @admin.register(Company) class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_permissions_enabled = True reverse_permission_mode = "hide" # or "disable" reverse_render_uses_field_policy = True reverse_relations = { "department_binding": ReverseRelationConfig( model=Department, fk_field="company", multiple=False, permission=staff_only_policy, ) } ``` -------------------------------- ### Configuring Reverse Relation with AJAX Widget in Django Admin Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Configures a reverse relation in Django admin to use a third-party AJAX search widget, specifically django-autocomplete-light's ModelSelect2, by providing the URL of the autocomplete view. ```python # In admin.py from dal import autocomplete @admin.register(Company) class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "department_binding": ReverseRelationConfig( model=Department, fk_field="company", # The widget is instantiated with the URL of the autocomplete view widget=autocomplete.ModelSelect2(url="department-autocomplete"), ) } fieldsets = (("Departments", {"fields": ("department_binding",)}),) class Media: js = ("admin/js/jquery.init.js",) # Ensure jQuery is loaded ``` -------------------------------- ### Extending ReverseRelationConfig: Custom Widgets Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/architecture.rst Shows how to provide custom widgets for reverse relation fields by setting the 'widget' attribute in ReverseRelationConfig to a widget instance or class, such as those from libraries like DAL or Unfold. ```python from django.contrib import admin from django_admin_reversefields.mixins import ReverseRelationAdminMixin from django_admin_reversefields.configs import ReverseRelationConfig from dal.widgets import ManyToManyDALWidget # Example from django-autocomplete-light class MyModelAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "m2m_field": ReverseRelationConfig( model="app.RelatedModel", foreign_key="my_model_fk", multiselect=True, widget=ManyToManyDALWidget, ), } ``` -------------------------------- ### Django Admin: Custom Reverse Change Permission Logic Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Demonstrates how to override the has_reverse_change_permission method in a Django admin to implement custom permission checks for reverse relations, including object-level permissions. ```python class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_permissions_enabled = True def has_reverse_change_permission(self, request, obj, config, selection=None): # Example: object-level check (works with backends that support object perms) app, model = config.model._meta.app_label, config.model._meta.model_name codename = f"{app}.change_{model}" if selection is None: return request.user.has_perm(codename) if isinstance(selection, (list, tuple)): return all(request.user.has_perm(codename, s) for s in selection) return request.user.has_perm(codename, selection) ``` -------------------------------- ### Django Admin: Filter QuerySet for OneToOneField Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Illustrates how to filter a queryset for a reverse OneToOneField relation using Q objects, specifically to show only unbound or currently related items. ```python def only_unbound_or_current(qs, instance, request): if instance and instance.pk: return qs.filter(Q(company__isnull=True) | Q(company=instance)) return qs.filter(company__isnull=True) @admin.register(Company) class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): ``` -------------------------------- ### Django Admin: Swap Permission Codename for Reverse Relations Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Illustrates how to change the permission required for reverse relation changes by modifying the codename used in has_reverse_change_permission, for instance, requiring 'add' permission instead of 'change'. ```python class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_permissions_enabled = True def has_reverse_change_permission(self, request, obj, config, selection=None): app, model = config.model._meta.app_label, config.model._meta.model_name return request.user.has_perm(f"{app}.add_{model}") # require add instead of change ``` -------------------------------- ### ReverseRelationAdminMixin: Persistence of Reverse Relations Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/architecture.rst Details how the mixin applies changes to reverse relations during the save process. It handles both single and multi-select fields, including unbinding and binding related objects. ```python from django.contrib import admin from django_admin_reversefields.mixins import ReverseRelationAdminMixin class MyModelAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "related_objects": ReverseRelationConfig(model="app.RelatedModel", foreign_key="my_model_fk", multiselect=True), } reverse_relations_atomic = True # Default behavior def save_model(self, request, obj, form, change): # The mixin's _apply_reverse_relations method is called after save_model super().save_model(request, obj, form, change) ``` -------------------------------- ### Customize Reverse Relation Widget Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/caveats.rst Override ReverseRelationConfig.widget to integrate custom widgets like Unfold or DAL for enhanced user experience or request-aware choice limiting. ```python from django_admin_reversefields.mixins import ReverseRelationConfig # Example using a hypothetical custom widget # from myapp.widgets import CustomSelectWidget # class MyModelAdmin(ReverseRelationAdminMixin): # reverse_fields = { # "my_relation": ReverseRelationConfig(widget=CustomSelectWidget) # } ``` -------------------------------- ### Configure Transactional Behavior for Reverse Relations Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/caveats.rst By default, reverse relation updates run within a single transaction. Opt-out only if partial failures are acceptable. Be aware of potential database-level uniqueness errors with concurrent edits. ```python from django_admin_reversefields.mixins import ReverseRelationAdminMixin class MyModelAdmin(ReverseRelationAdminMixin): reverse_relations_atomic = True # Defaults to True # To opt-out: # reverse_relations_atomic = False ``` -------------------------------- ### Request-Aware Validation Hook (Python) Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Implements validation logic that depends on the current user or request context. The `clean` hook receives the request object, allowing for user-specific rules and raising `forms.ValidationError` if conditions are not met. ```python from django import forms from django.contrib import admin from django_admin_reversefields.mixins import ReverseRelationAdminMixin, ReverseRelationConfig def staff_only(instance, selection, request): if not getattr(request, "user", None) or not request.user.is_staff: raise forms.ValidationError("Not permitted") @admin.register(Company) class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "department_binding": ReverseRelationConfig( model=Department, fk_field="company", multiple=False, required=False, clean=staff_only, # gets (instance, selection, request) ) } ``` -------------------------------- ### ReverseRelationAdminMixin: Form Construction with Virtual Fields Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/architecture.rst Demonstrates how ReverseRelationAdminMixin modifies the Django admin form by injecting ModelChoiceField or ModelMultipleChoiceField for configured reverse relations. It handles stripping virtual field names from the base form fields to prevent errors. ```python from django.contrib import admin from django_admin_reversefields.mixins import ReverseRelationAdminMixin class MyModelAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "virtual_field_name": ReverseRelationConfig(model="app.OtherModel", foreign_key="my_fk"), } def get_form(self, request, obj=None, **kwargs): # The mixin's get_form handles injecting the correct fields return super().get_form(request, obj, **kwargs) ``` -------------------------------- ### ReverseRelationAdminMixin: Deferring Reverse Updates with commit=False Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/architecture.rst Explains how to handle reverse relation updates when a form is saved with 'commit=False'. The mixin defers these updates until the ModelAdmin's save_model method is called, storing authorized reverse field payloads on the form instance. ```python from django.contrib import admin from django_admin_reversefields.mixins import ReverseRelationAdminMixin class MyModelAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_relations = { "related_data": ReverseRelationConfig(model="app.DataModel", foreign_key="my_fk"), } def save_model(self, request, obj, form, change): # If commit=False was used, the mixin stores updates and applies them here. super().save_model(request, obj, form, change) ``` -------------------------------- ### Handle Nullable Reverse ForeignKey for Unbinding Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/caveats.rst Ensures the reverse ForeignKey is nullable to allow unbinding. If the column is not nullable, set required=True on ReverseRelationConfig to prevent unbind attempts and avoid IntegrityError. ```python from django_admin_reversefields.mixins import ReverseRelationConfig # Ensure the reverse ForeignKey is nullable # If not nullable, set required=True on ReverseRelationConfig # Example: # class MyModelAdmin(ReverseRelationAdminMixin): # reverse_fields = { # "my_relation": ReverseRelationConfig(required=True) # } ``` -------------------------------- ### Enable Reverse Permissions Source: https://github.com/tnware/django-admin-reversefields/blob/main/README.md Configures permission handling for reverse relations within a Django admin model. It allows disabling or hiding fields based on user permissions. ```python class ServiceAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_permissions_enabled = True reverse_permission_mode = "disable" # or "hide" ``` -------------------------------- ### Permissions: Disable on Reverse Field Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/recipes.rst Configures permission handling for reverse fields, specifically disabling the field if the user lacks the 'change' permission on the related model. This uses 'reverse_permissions_enabled' and 'reverse_permission_mode'. ```python @admin.register(Company) class CompanyAdmin(ReverseRelationAdminMixin, admin.ModelAdmin): reverse_permissions_enabled = True reverse_permission_mode = "disable" # or "hide" reverse_relations = { "department_binding": ReverseRelationConfig( model=Department, fk_field="company", multiple=False, ) } # Optional: let per-field/global policies decide visibility at render time # (policy must handle selection=None) # reverse_render_uses_field_policy = True ``` -------------------------------- ### Override has_reverse_change_permission for Custom Permissions Source: https://github.com/tnware/django-admin-reversefields/blob/main/docs/architecture.rst Implement the `has_reverse_change_permission` method to enforce custom permission codenames or object-level checks for reverse relations. ```python from django_admin_reversefields.mixins import ReverseRelationAdminMixin class MyModelAdmin(ReverseRelationAdminMixin): def has_reverse_change_permission(self, request, obj, related_obj): # Your custom permission logic here # For example, checking specific permissions for related_obj return request.user.has_perm('my_app.can_change_related_item') ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.