1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
|
import copy
from typing import List, Union
from urllib import parse as urlparse
import django
from django.forms import widgets
from django.utils.functional import Promise
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django_countries.conf import settings
COUNTRY_CHANGE_HANDLER = (
"var e=document.getElementById('flag_' + this.id); "
"if (e) e.src = '%s'"
".replace('{code}', this.value.toLowerCase() || '__')"
".replace('{code_upper}', this.value.toUpperCase() || '__');"
)
ChoiceList = List[List[Union[int, str]]]
class LazyChoicesMixin:
if django.VERSION < (5, 0):
def get_choices(self) -> ChoiceList:
"""
When it's time to get the choices, if it was a lazy then figure it out
now and memoize the result.
"""
if isinstance(self._choices, Promise):
self._choices: ChoiceList = list(self._choices)
return self._choices
def set_choices(self, value: ChoiceList):
self._set_choices(value)
choices = property(get_choices, set_choices)
def _set_choices(self, value: ChoiceList):
self._choices = value
class LazySelectMixin(LazyChoicesMixin):
if django.VERSION < (5, 0):
def __deepcopy__(self, memo):
obj = copy.copy(self)
obj.attrs = self.attrs.copy()
obj.choices = copy.copy(self._choices)
memo[id(self)] = obj
return obj
class LazySelect(LazySelectMixin, widgets.Select): # type: ignore
"""
A form Select widget that respects choices being a lazy object.
"""
class LazySelectMultiple(LazySelectMixin, widgets.SelectMultiple): # type: ignore
"""
A form SelectMultiple widget that respects choices being a lazy object.
"""
class CountrySelectWidget(LazySelect):
def __init__(self, *args, **kwargs) -> None:
self.layout = kwargs.pop("layout", None) or (
'{widget}<img class="country-select-flag" id="{flag_id}" '
'style="margin: 6px 4px 0" '
'src="{country.flag}">'
)
super().__init__(*args, **kwargs)
def render(self, name, value, attrs=None, renderer=None):
from django_countries.fields import Country
attrs = attrs or {}
widget_id = attrs and attrs.get("id")
if widget_id:
flag_id = f"flag_{widget_id}"
attrs["onchange"] = COUNTRY_CHANGE_HANDLER % urlparse.urljoin(
settings.STATIC_URL, settings.COUNTRIES_FLAG_URL
)
else:
flag_id = ""
widget_render = super().render(name, value, attrs, renderer=renderer)
country = value if isinstance(value, Country) else Country(value or "__")
with country.escape:
return mark_safe( # nosec
self.layout.format(
widget=widget_render, country=country, flag_id=escape(flag_id)
)
)
|