Source code for django_choices_js.models
from dataclasses import dataclass
from typing import Optional, Literal
from django.apps import apps
import django.db.models
Lookup = Literal[
'exact',
'iexact',
'contains',
'icontains',
'startswith',
'istartswith',
'endswith',
'iendswith',
'regex',
'iregex',
]
default_lookups: list[Lookup] = ['icontains']
[docs]
@dataclass(init=True, repr=True)
class ChoicesAutocompletePathConfig:
path: str
fields: dict[str, list[Lookup]]
permission: Optional[str]
[docs]
def autocomplete_path_for_model(model: django.db.models.Model, name: Optional[str] = None):
# noinspection PyProtectedMember
path = f'{model._meta.app_label}__{model.__name__.lower()}'
if name is not None:
path += f'__{name}'
return path
[docs]
def autocomplete_model(fields: list[str] | dict[str, list[Lookup]], name: Optional[str] = None, permission: Optional[str] = None):
"""
Decorator that registers an autocompletion endpoint for a given model.
:param fields: The fields that should be searched for autocompletion.
Can be either a list of field names or a dict with field names as keys and a list of `lookup functions`_ as values.
If a list is passed, *icontains* will be used as the default lookup function.
:param name: Optional identifier for the autocomplete path. If None, it will become the default path for the model.
:param permission: Required django permission to use the autocompletion endpoint. If None, it will be accessible to everyone.
Examples::
@autocomplete_model(fields=['name'])
@autocomplete_model(fields={'name': ['istartswith', 'iendswith']}, name='prefix_or_suffix')
.. _lookup functions: https://docs.djangoproject.com/en/6.1/ref/models/querysets/#field-lookups
"""
def decorator(cls):
path = autocomplete_path_for_model(cls, name)
registered_paths = getattr(cls, '__django_choices_js__') if hasattr(cls, '__django_choices_js__') else {}
if path in registered_paths:
raise AutocompletePathAlreadyRegistered(
f'autocomplete path {path} already registered for class {cls.__name__}'
)
config = apps.get_app_config('django_choices_js')
config.autocomplete_paths[path] = cls
registered_paths[path] = ChoicesAutocompletePathConfig(
path=path,
fields=fields if isinstance(fields, dict) else {field: default_lookups for field in fields},
permission=permission
)
setattr(cls, '__django_choices_js__', registered_paths)
return cls
return decorator
[docs]
class AutocompletePathAlreadyRegistered(Exception):
def __init__(self, message):
self.message = message