A Django application that provides country choices for use with forms, flag icons static files, and a country field for models.
—
Serializer fields and mixins for Django REST Framework providing country serialization with flexible output formats. The integration supports code-only, name-only, and dictionary representations with automatic model field mapping.
Specialized DRF serializer field for country data with flexible output formatting options.
class CountryField(serializers.ChoiceField):
def __init__(
self,
*args,
country_dict=None,
name_only=None,
countries=None,
**kwargs
):
"""
DRF serializer field for country representation.
Parameters:
- country_dict: bool - Return country as {'code': 'US', 'name': 'United States'}
- name_only: bool - Return only country name instead of code
- countries: Countries - Custom countries instance
- **kwargs: Standard ChoiceField arguments
"""
def to_representation(self, obj):
"""
Convert country code to serialized representation.
Parameters:
- obj: Country code or Country object
Returns:
- Serialized country data based on field configuration
"""
def to_internal_value(self, data):
"""
Convert serialized data to country code.
Parameters:
- data: Serialized country data (code, name, or dict)
Returns:
- str: Country code for internal storage
"""Automatic field mapping mixin for ModelSerializer classes that converts CountryField model fields to appropriate serializer fields.
class CountryFieldMixin:
def build_standard_field(self, field_name, model_field):
"""
Build appropriate serializer field for CountryField model fields.
Parameters:
- field_name: str - Name of the field
- model_field: CountryField - Model field instance
Returns:
- Tuple[Field, dict]: Serializer field class and kwargs
"""from rest_framework import serializers
from django_countries.serializer_fields import CountryField
class PersonSerializer(serializers.Serializer):
name = serializers.CharField()
# Return country code only (default)
country = CountryField()
# Return country name only
birth_country = CountryField(name_only=True)
# Return country as dictionary with code and name
residence = CountryField(country_dict=True)
# Sample output:
# {
# "name": "John Doe",
# "country": "US",
# "birth_country": "United States",
# "residence": {"code": "US", "name": "United States"}
# }from rest_framework import serializers
from django_countries.serializers import CountryFieldMixin
from myapp.models import Person
class PersonModelSerializer(CountryFieldMixin, serializers.ModelSerializer):
"""
ModelSerializer with automatic CountryField mapping.
CountryFieldMixin automatically converts model CountryFields to serializer CountryFields.
"""
class Meta:
model = Person
fields = ['name', 'country', 'visited_countries']
# Alternative manual configuration
class PersonCustomSerializer(serializers.ModelSerializer):
country = CountryField(country_dict=True)
visited_countries = serializers.ListField(
child=CountryField(name_only=True)
)
class Meta:
model = Person
fields = ['name', 'country', 'visited_countries']from django_countries import Countries
from django_countries.serializer_fields import CountryField
# Create custom countries list
eu_countries = Countries()
eu_countries.only = ["DE", "FR", "IT", "ES", "NL", "BE"]
class EuropeanEventSerializer(serializers.Serializer):
title = serializers.CharField()
country = CountryField(countries=eu_countries)The CountryField accepts multiple input formats:
# Valid input formats for CountryField:
# 1. Country code (alpha2, alpha3, numeric)
{"country": "US"}
{"country": "USA"}
{"country": "840"}
# 2. Country name (exact match)
{"country": "United States"}
# 3. Dictionary format
{"country": {"code": "US"}}
{"country": {"code": "US", "name": "United States"}}class FlexiblePersonSerializer(serializers.Serializer):
name = serializers.CharField()
# Code only (default): "US"
country_code = CountryField()
# Name only: "United States"
country_name = CountryField(name_only=True)
# Dictionary: {"code": "US", "name": "United States"}
country_full = CountryField(country_dict=True)from rest_framework import serializers
from django_countries.serializer_fields import CountryField
class OrganizationSerializer(serializers.ModelSerializer):
# Multiple countries as list of codes
operating_countries = serializers.ListField(
child=CountryField()
)
# Multiple countries as list of names
operating_regions = serializers.ListField(
child=CountryField(name_only=True)
)
# Multiple countries as list of dictionaries
detailed_countries = serializers.ListField(
child=CountryField(country_dict=True)
)
# Sample output:
# {
# "operating_countries": ["US", "CA", "MX"],
# "operating_regions": ["United States", "Canada", "Mexico"],
# "detailed_countries": [
# {"code": "US", "name": "United States"},
# {"code": "CA", "name": "Canada"},
# {"code": "MX", "name": "Mexico"}
# ]
# }class StrictCountrySerializer(serializers.Serializer):
country = CountryField()
def validate_country(self, value):
"""Custom country validation."""
if value not in ["US", "CA", "MX"]:
raise serializers.ValidationError(
"Only North American countries are allowed"
)
return value
# Invalid country codes raise ValidationError:
# serializer = StrictCountrySerializer(data={"country": "INVALID"})
# serializer.is_valid() # False
# serializer.errors # {"country": ["Select a valid choice..."]}from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
class PersonViewSet(viewsets.ModelViewSet):
serializer_class = PersonModelSerializer
@action(detail=False, methods=['get'])
def countries(self, request):
"""Return list of all available countries."""
from django_countries import countries
country_list = [
{"code": country.code, "name": country.name}
for country in countries
]
return Response(country_list)
@action(detail=False, methods=['get'])
def by_country(self, request):
"""Filter people by country."""
country_code = request.query_params.get('country')
if country_code:
queryset = self.get_queryset().filter(country=country_code)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
return Response([])class ExtendedCountryField(CountryField):
"""Custom country field with additional metadata."""
def to_representation(self, obj):
"""Add additional country metadata."""
from django_countries import countries
code = countries.alpha2(obj)
if not code:
return ""
return {
"code": code,
"name": countries.name(code),
"alpha3": countries.alpha3(code),
"numeric": countries.numeric(code),
"flag_url": f"/static/flags/{code.lower()}.gif"
}
class DetailedPersonSerializer(serializers.Serializer):
name = serializers.CharField()
country = ExtendedCountryField()
# Output:
# {
# "name": "John Doe",
# "country": {
# "code": "US",
# "name": "United States",
# "alpha3": "USA",
# "numeric": 840,
# "flag_url": "/static/flags/us.gif"
# }
# }from django_filter import rest_framework as filters
class PersonFilter(filters.FilterSet):
country = filters.CharFilter(field_name='country')
country_name = filters.CharFilter(
field_name='country',
lookup_expr='name_icontains'
)
class Meta:
model = Person
fields = ['country', 'country_name']
class PersonViewSet(viewsets.ModelViewSet):
serializer_class = PersonModelSerializer
filterset_class = PersonFilter
# URL examples:
# /api/people/?country=US
# /api/people/?country_name=unitedclass PersonV1Serializer(serializers.ModelSerializer):
"""v1 API returns country codes only."""
country = CountryField()
class Meta:
model = Person
fields = ['name', 'country']
class PersonV2Serializer(serializers.ModelSerializer):
"""v2 API returns country with full details."""
country = CountryField(country_dict=True)
class Meta:
model = Person
fields = ['name', 'country']class AddressSerializer(serializers.Serializer):
street = serializers.CharField()
city = serializers.CharField()
country = CountryField(country_dict=True)
class PersonWithAddressSerializer(serializers.Serializer):
name = serializers.CharField()
address = AddressSerializer()
# Output:
# {
# "name": "John Doe",
# "address": {
# "street": "123 Main St",
# "city": "New York",
# "country": {"code": "US", "name": "United States"}
# }
# }class OptimizedPersonSerializer(serializers.ModelSerializer):
"""Optimized serializer for bulk operations."""
# Use code-only for better performance in lists
country = CountryField()
def to_representation(self, instance):
"""Add country name only when needed."""
data = super().to_representation(instance)
# Add country name for detail views
if self.context.get('detail_view'):
from django_countries import countries
data['country_name'] = countries.name(data['country'])
return dataInstall with Tessl CLI
npx tessl i tessl/pypi-django-countries