from itertools import chain from functools import cached_property from django import forms, http from django.urls import reverse __all__ = ( "VueWidget", "VueAutoComplete", ) class VueWidget(forms.Widget): binds = None """Dict of `{attribute: value}` attrs set as bindings.""" events = None """Dict of `{event: value}` attrs set as events.""" v_model = "" """ES6 Model instance to bind to (`v-model`).""" def __init__(self, *args, binds=None, events=None, v_model=None, **kwargs): super().__init__(*args, **kwargs) self.binds = binds or [] self.events = events or [] @cached_property def vue_attrs(self): """Dict of Vue specific attributes.""" binds, events = self.binds, self.events if isinstance(binds, dict): binds = binds.items() if isinstance(events, dict): events = events.items() return dict( chain( ((":" + key, value) for key, value in binds), (("@" + key, value) for key, value in events), ) ) def build_attrs(self, base_attrs, extra_attrs=None): extra_attrs = extra_attrs or {} extra_attrs.update(self.vue_attrs) return super().build_attrs(base_attrs, extra_attrs) class VueAutoComplete(VueWidget, forms.TextInput): """Autocomplete Vue component.""" template_name = "aircox/widgets/autocomplete.html" url: str = "" """Url to autocomplete API view. If it has query parameters, does not generate it based on lookup (see `get_url()` doc). """ lookup: str = "" """Field name used as lookup (instead as provided one).""" params: http.QueryDict def __init__(self, url_name, *args, lookup=None, params=None, **kwargs): self.url_name = url_name self.lookup = lookup self.params = params super().__init__(*args, **kwargs) def get_context(self, name, value, attrs): context = super().get_context(name, value, attrs) context["url"] = self.get_url(name, self.lookup, self.params) return context def get_url(self, name, lookup, params=None): """Return url to autocomplete API. When query parameters are not provided generate them using `?{lookup}=${query}&field={name}` (where `${query} is Vue `a-autocomplete` specific). :param str name: field name (not used by default) :param str lookup: lookup query parameter :param http.QueryDict params: additional mutable parameter """ url = reverse(self.url_name) query = http.QueryDict(mutable=True) if params: query.update(params) query.update({lookup: "${query}"}) return f"{url}?{query.urlencode()}"