A Django application that provides country choices for use with forms, flag icons static files, and a country field for models.
—
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.
Django form fields that provide country selection with lazy-loaded choices and proper validation.
class LazyTypedChoiceField(forms.TypedChoiceField):
"""
Form field for single country selection with lazy-loaded country choices.
"""
choices: Any
widget: LazySelect
class LazyTypedMultipleChoiceField(forms.TypedMultipleChoiceField):
"""
Form field for multiple country selection with lazy-loaded country choices.
"""
choices: Any
widget: LazySelectMultipleDjango form widgets with enhanced country selection interfaces and flag display capabilities.
class LazySelect(widgets.Select):
"""
Select widget with lazy-loaded country choices.
"""
def __init__(self, attrs=None, choices=()):
"""
Initialize select widget with lazy choice loading.
Parameters:
- attrs: dict - HTML attributes for widget
- choices: iterable - Choice options (loaded lazily)
"""
class LazySelectMultiple(widgets.SelectMultiple):
"""
Multiple select widget with lazy-loaded country choices.
"""
def __init__(self, attrs=None, choices=()):
"""
Initialize multiple select widget with lazy choice loading.
Parameters:
- attrs: dict - HTML attributes for widget
- choices: iterable - Choice options (loaded lazily)
"""
class CountrySelectWidget(LazySelect):
"""
Enhanced country select widget with flag display and JavaScript integration.
"""
def __init__(
self,
layout=None,
attrs=None,
choices=(),
flag_url=None
):
"""
Initialize country select widget with flag support.
Parameters:
- layout: str - Widget layout template
- attrs: dict - HTML attributes for widget
- choices: iterable - Country choices
- flag_url: str - Flag image URL pattern
"""from django import forms
from django_countries.fields import LazyTypedChoiceField, LazyTypedMultipleChoiceField
from django_countries import countries
class PersonForm(forms.Form):
# Single country selection
country = LazyTypedChoiceField(choices=countries)
# Multiple country selection
visited_countries = LazyTypedMultipleChoiceField(
choices=countries,
required=False
)
class EventForm(forms.Form):
# With custom widget attributes
country = LazyTypedChoiceField(
choices=countries,
widget=forms.Select(attrs={
'class': 'form-control',
'data-placeholder': 'Select country'
})
)from django import forms
from django_countries.widgets import CountrySelectWidget
from myapp.models import Organization
class OrganizationForm(forms.ModelForm):
class Meta:
model = Organization
fields = ['name', 'country', 'operating_countries']
widgets = {
'country': CountrySelectWidget(attrs={
'class': 'country-select'
}),
'operating_countries': forms.SelectMultiple(attrs={
'class': 'multi-country-select'
})
}from django_countries.widgets import CountrySelectWidget
class CustomCountryForm(forms.Form):
country = forms.CharField(
widget=CountrySelectWidget(
flag_url="custom/flags/{code}.png",
attrs={
'class': 'custom-country-widget',
'onchange': 'updateFlag(this.value)'
}
)
)The CountrySelectWidget includes JavaScript for dynamic flag display:
from django_countries.widgets import CountrySelectWidget
# Widget with flag support
widget = CountrySelectWidget(
flag_url="flags/{code_upper}.png",
attrs={'id': 'country_select'}
)
# Generated JavaScript automatically updates flag images
# with ID pattern: 'flag_' + widget_idHTML template example:
<form>
{{ form.country }}
<img id="flag_country_select" src="flags/__.png" alt="Flag">
</form>Widgets support lazy choice loading to avoid loading country data until needed:
from django_countries.widgets import LazySelect
from django_countries import countries
# Choices loaded only when widget is rendered
widget = LazySelect(choices=countries)
# Works with filtered countries too
filtered_countries = Countries()
filtered_countries.only = ["US", "CA", "MX"]
widget = LazySelect(choices=filtered_countries)from django_countries import Countries
# Create custom countries instance
asia_countries = Countries()
asia_countries.only = ["CN", "JP", "KR", "IN", "TH", "VN"]
class AsiaEventForm(forms.Form):
country = LazyTypedChoiceField(
choices=asia_countries,
widget=CountrySelectWidget()
)Mixin providing lazy choice loading functionality for Django < 5.0 compatibility:
class LazyChoicesMixin:
"""Mixin for widgets that support lazy-loaded choices."""
def get_choices(self) -> List[List[Union[int, str]]]:
"""Get choices, evaluating lazy choices if needed."""
def set_choices(self, value):
"""Set widget choices."""
choices = property(get_choices, set_choices)Combined mixin for select widgets with lazy choices:
class LazySelectMixin(LazyChoicesMixin):
"""Mixin combining lazy choices with select widget functionality."""
def __deepcopy__(self, memo):
"""Deep copy widget with choices preservation."""Configure different flag image patterns:
# Different flag formats
widget1 = CountrySelectWidget(flag_url="flags/4x3/{code}.svg")
widget2 = CountrySelectWidget(flag_url="https://flags.api.com/{code_upper}.png")
widget3 = CountrySelectWidget(flag_url="assets/country-flags/{code}-flag.gif")Available template variables:
{code}: lowercase country code (e.g., "us"){code_upper}: uppercase country code (e.g., "US")The widget automatically generates JavaScript for flag updates:
// Auto-generated JavaScript pattern
var e=document.getElementById('flag_' + this.id);
if (e) e.src = 'flags/{code}.gif'
.replace('{code}', this.value.toLowerCase() || '__')
.replace('{code_upper}', this.value.toUpperCase() || '__');For advanced customization, create custom widget templates:
class CustomCountryWidget(CountrySelectWidget):
template_name = 'custom_widgets/country_select.html'
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context['flag_url_pattern'] = self.flag_url
return contextfrom crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field
class CountryForm(forms.Form):
country = LazyTypedChoiceField(choices=countries)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Field('country', css_class='country-select')
)class StrictCountryForm(forms.Form):
country = LazyTypedChoiceField(choices=countries)
def clean_country(self):
country = self.cleaned_data['country']
if not country:
raise forms.ValidationError("Country is required")
# Additional validation
if country not in ["US", "CA", "MX"]:
raise forms.ValidationError("Only North American countries allowed")
return countryclass DynamicCountryForm(forms.Form):
def __init__(self, *args, region=None, **kwargs):
super().__init__(*args, **kwargs)
if region == 'europe':
eu_countries = Countries()
eu_countries.only = ["DE", "FR", "IT", "ES", "NL", "BE"]
self.fields['country'] = LazyTypedChoiceField(choices=eu_countries)
else:
self.fields['country'] = LazyTypedChoiceField(choices=countries)Install with Tessl CLI
npx tessl i tessl/pypi-django-countries