default_app_config = 'autocomplete.apps.AutocompleteConfig' from django.db import models from django.core.exceptions import ImproperlyConfigured from django.forms.widgets import Widget from django.template.loader import render_to_string class ModelAutocomplete: """Base class for model-based autocomplete.""" model = None # Model class to use for autocomplete search_attrs = [] # List of model attributes to search minimum_search_length = 2 # Minimum length of search string max_results = 10 # Maximum number of results to return def __init__(self): if not self.model: raise ImproperlyConfigured("ModelAutocomplete requires a model class") if not self.search_attrs: raise ImproperlyConfigured("ModelAutocomplete requires search_attrs") def get_search_results(self, search): """Return search results for a given search string.""" raise NotImplementedError("Subclasses must implement get_search_results()") def format_result(self, obj): """Format a single result object.""" raise NotImplementedError("Subclasses must implement format_result()") class AutocompleteWidget(Widget): """Widget for autocomplete fields.""" template_name = 'autocomplete/widget.html' def __init__(self, ac_class, attrs=None): super().__init__(attrs) if not issubclass(ac_class, ModelAutocomplete): raise ImproperlyConfigured("ac_class must be a subclass of ModelAutocomplete") self.ac_class = ac_class def get_context(self, name, value, attrs): context = super().get_context(name, value, attrs) # Add ac_name for URL resolution context['ac_name'] = self.ac_class.__name__.lower() return context def render(self, name, value, attrs=None, renderer=None): context = self.get_context(name, value, attrs) return render_to_string(self.template_name, context)