File: widgets.py

package info (click to toggle)
django-countries 7.6.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,400 kB
  • sloc: python: 3,425; sh: 29; makefile: 17
file content (95 lines) | stat: -rw-r--r-- 3,011 bytes parent folder | download | duplicates (2)
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)
                )
            )