0
# Form Fields and Widgets
1
2
Specialized Django form fields and widgets for country selection with lazy-loaded choices, flag display support, and JavaScript integration. These components provide enhanced user interfaces for country selection in Django forms.
3
4
## Capabilities
5
6
### Form Fields
7
8
Django form fields that provide country selection with lazy-loaded choices and proper validation.
9
10
```python { .api }
11
class LazyTypedChoiceField(forms.TypedChoiceField):
12
"""
13
Form field for single country selection with lazy-loaded country choices.
14
"""
15
choices: Any
16
widget: LazySelect
17
18
class LazyTypedMultipleChoiceField(forms.TypedMultipleChoiceField):
19
"""
20
Form field for multiple country selection with lazy-loaded country choices.
21
"""
22
choices: Any
23
widget: LazySelectMultiple
24
```
25
26
### Widgets
27
28
Django form widgets with enhanced country selection interfaces and flag display capabilities.
29
30
```python { .api }
31
class LazySelect(widgets.Select):
32
"""
33
Select widget with lazy-loaded country choices.
34
"""
35
def __init__(self, attrs=None, choices=()):
36
"""
37
Initialize select widget with lazy choice loading.
38
39
Parameters:
40
- attrs: dict - HTML attributes for widget
41
- choices: iterable - Choice options (loaded lazily)
42
"""
43
44
class LazySelectMultiple(widgets.SelectMultiple):
45
"""
46
Multiple select widget with lazy-loaded country choices.
47
"""
48
def __init__(self, attrs=None, choices=()):
49
"""
50
Initialize multiple select widget with lazy choice loading.
51
52
Parameters:
53
- attrs: dict - HTML attributes for widget
54
- choices: iterable - Choice options (loaded lazily)
55
"""
56
57
class CountrySelectWidget(LazySelect):
58
"""
59
Enhanced country select widget with flag display and JavaScript integration.
60
"""
61
def __init__(
62
self,
63
layout=None,
64
attrs=None,
65
choices=(),
66
flag_url=None
67
):
68
"""
69
Initialize country select widget with flag support.
70
71
Parameters:
72
- layout: str - Widget layout template
73
- attrs: dict - HTML attributes for widget
74
- choices: iterable - Country choices
75
- flag_url: str - Flag image URL pattern
76
"""
77
```
78
79
## Usage Examples
80
81
### Basic Form Usage
82
83
```python
84
from django import forms
85
from django_countries.fields import LazyTypedChoiceField, LazyTypedMultipleChoiceField
86
from django_countries import countries
87
88
class PersonForm(forms.Form):
89
# Single country selection
90
country = LazyTypedChoiceField(choices=countries)
91
92
# Multiple country selection
93
visited_countries = LazyTypedMultipleChoiceField(
94
choices=countries,
95
required=False
96
)
97
98
class EventForm(forms.Form):
99
# With custom widget attributes
100
country = LazyTypedChoiceField(
101
choices=countries,
102
widget=forms.Select(attrs={
103
'class': 'form-control',
104
'data-placeholder': 'Select country'
105
})
106
)
107
```
108
109
### Model Form Integration
110
111
```python
112
from django import forms
113
from django_countries.widgets import CountrySelectWidget
114
from myapp.models import Organization
115
116
class OrganizationForm(forms.ModelForm):
117
class Meta:
118
model = Organization
119
fields = ['name', 'country', 'operating_countries']
120
widgets = {
121
'country': CountrySelectWidget(attrs={
122
'class': 'country-select'
123
}),
124
'operating_countries': forms.SelectMultiple(attrs={
125
'class': 'multi-country-select'
126
})
127
}
128
```
129
130
### Custom Widget Configuration
131
132
```python
133
from django_countries.widgets import CountrySelectWidget
134
135
class CustomCountryForm(forms.Form):
136
country = forms.CharField(
137
widget=CountrySelectWidget(
138
flag_url="custom/flags/{code}.png",
139
attrs={
140
'class': 'custom-country-widget',
141
'onchange': 'updateFlag(this.value)'
142
}
143
)
144
)
145
```
146
147
## Widget Features
148
149
### Flag Display Integration
150
151
The CountrySelectWidget includes JavaScript for dynamic flag display:
152
153
```python
154
from django_countries.widgets import CountrySelectWidget
155
156
# Widget with flag support
157
widget = CountrySelectWidget(
158
flag_url="flags/{code_upper}.png",
159
attrs={'id': 'country_select'}
160
)
161
162
# Generated JavaScript automatically updates flag images
163
# with ID pattern: 'flag_' + widget_id
164
```
165
166
HTML template example:
167
```html
168
<form>
169
{{ form.country }}
170
<img id="flag_country_select" src="flags/__.png" alt="Flag">
171
</form>
172
```
173
174
### Lazy Choice Loading
175
176
Widgets support lazy choice loading to avoid loading country data until needed:
177
178
```python
179
from django_countries.widgets import LazySelect
180
from django_countries import countries
181
182
# Choices loaded only when widget is rendered
183
widget = LazySelect(choices=countries)
184
185
# Works with filtered countries too
186
filtered_countries = Countries()
187
filtered_countries.only = ["US", "CA", "MX"]
188
widget = LazySelect(choices=filtered_countries)
189
```
190
191
### Custom Choice Lists
192
193
```python
194
from django_countries import Countries
195
196
# Create custom countries instance
197
asia_countries = Countries()
198
asia_countries.only = ["CN", "JP", "KR", "IN", "TH", "VN"]
199
200
class AsiaEventForm(forms.Form):
201
country = LazyTypedChoiceField(
202
choices=asia_countries,
203
widget=CountrySelectWidget()
204
)
205
```
206
207
## Widget Mixins
208
209
### LazyChoicesMixin
210
211
Mixin providing lazy choice loading functionality for Django < 5.0 compatibility:
212
213
```python { .api }
214
class LazyChoicesMixin:
215
"""Mixin for widgets that support lazy-loaded choices."""
216
217
def get_choices(self) -> List[List[Union[int, str]]]:
218
"""Get choices, evaluating lazy choices if needed."""
219
220
def set_choices(self, value):
221
"""Set widget choices."""
222
223
choices = property(get_choices, set_choices)
224
```
225
226
### LazySelectMixin
227
228
Combined mixin for select widgets with lazy choices:
229
230
```python { .api }
231
class LazySelectMixin(LazyChoicesMixin):
232
"""Mixin combining lazy choices with select widget functionality."""
233
234
def __deepcopy__(self, memo):
235
"""Deep copy widget with choices preservation."""
236
```
237
238
## Advanced Widget Customization
239
240
### Custom Flag URL Patterns
241
242
Configure different flag image patterns:
243
244
```python
245
# Different flag formats
246
widget1 = CountrySelectWidget(flag_url="flags/4x3/{code}.svg")
247
widget2 = CountrySelectWidget(flag_url="https://flags.api.com/{code_upper}.png")
248
widget3 = CountrySelectWidget(flag_url="assets/country-flags/{code}-flag.gif")
249
```
250
251
Available template variables:
252
- `{code}`: lowercase country code (e.g., "us")
253
- `{code_upper}`: uppercase country code (e.g., "US")
254
255
### JavaScript Integration
256
257
The widget automatically generates JavaScript for flag updates:
258
259
```javascript
260
// Auto-generated JavaScript pattern
261
var e=document.getElementById('flag_' + this.id);
262
if (e) e.src = 'flags/{code}.gif'
263
.replace('{code}', this.value.toLowerCase() || '__')
264
.replace('{code_upper}', this.value.toUpperCase() || '__');
265
```
266
267
### Custom Widget Templates
268
269
For advanced customization, create custom widget templates:
270
271
```python
272
class CustomCountryWidget(CountrySelectWidget):
273
template_name = 'custom_widgets/country_select.html'
274
275
def get_context(self, name, value, attrs):
276
context = super().get_context(name, value, attrs)
277
context['flag_url_pattern'] = self.flag_url
278
return context
279
```
280
281
## Form Integration Patterns
282
283
### With Django Crispy Forms
284
285
```python
286
from crispy_forms.helper import FormHelper
287
from crispy_forms.layout import Layout, Field
288
289
class CountryForm(forms.Form):
290
country = LazyTypedChoiceField(choices=countries)
291
292
def __init__(self, *args, **kwargs):
293
super().__init__(*args, **kwargs)
294
self.helper = FormHelper()
295
self.helper.layout = Layout(
296
Field('country', css_class='country-select')
297
)
298
```
299
300
### Custom Validation
301
302
```python
303
class StrictCountryForm(forms.Form):
304
country = LazyTypedChoiceField(choices=countries)
305
306
def clean_country(self):
307
country = self.cleaned_data['country']
308
if not country:
309
raise forms.ValidationError("Country is required")
310
311
# Additional validation
312
if country not in ["US", "CA", "MX"]:
313
raise forms.ValidationError("Only North American countries allowed")
314
315
return country
316
```
317
318
### Dynamic Widget Configuration
319
320
```python
321
class DynamicCountryForm(forms.Form):
322
def __init__(self, *args, region=None, **kwargs):
323
super().__init__(*args, **kwargs)
324
325
if region == 'europe':
326
eu_countries = Countries()
327
eu_countries.only = ["DE", "FR", "IT", "ES", "NL", "BE"]
328
self.fields['country'] = LazyTypedChoiceField(choices=eu_countries)
329
else:
330
self.fields['country'] = LazyTypedChoiceField(choices=countries)
331
```