django-countries

repository·main·Indexed 23 days ago

https://github.com/smileychris/django-countries

A Django application providing a specialized CountryField for models, country choices for forms, and static flag icons. It includes support for ISO 3166-1 country data, translated names via Django's i18n system, and integrations with Django REST Framework, graphene-django, and django-filter. Version 9.0.0 features include the countries_context manager for dynamic configuration overrides and a Countries class for retrieving alpha2, alpha3, numeric, and IOC codes.

Tokens
23.6K
Snippets
80
Records
118
Agent score
75%

What's inside django-countries

  1. Overview of django-countries features

    main

    Django Countries provides several key capabilities for Django developers:

    • Country Field: A dedicated Django model field containing all ISO 3166-1 countries.
    • Translated Names: Country names are integrated with Django's i18n system for translation.
    • Flag Icons: Includes static flag image files for all countries.
    • REST Framework: Full integration with Django REST Framework.
    • GraphQL: Support for graphene-django.
    • Multiple Selection: Support for selecting multiple countries.
  2. Use Common Names for user-friendly country labels

    main

    By default, django-countries provides user-friendly alternatives for verbose official ISO names via the COMMON_NAMES dictionary. This is useful for simplifying names like Bolivia (Plurinational State of) to just Bolivia or Korea (the Democratic People's Republic of) to North Korea.

    When the COUNTRIES_COMMON_NAMES setting is enabled (which is the default), these friendly names are used in dropdown lists and displays instead of the official ISO names.

  3. Performance considerations for dynamic ordering

    main

    Dynamic country ordering is optimized for performance using a caching mechanism:

    • First call cost: The initial iteration for a specific language/ordering combination incurs a cost for translation and sorting.
    • Subsequent calls: Once computed, results are fully cached and very fast.
    • Memory usage: Each unique combination of language and ordering configuration creates exactly one cache entry.
    • Thread safety: The implementation uses asgiref.local.Local to ensure thread-local context safety.

    This caching strategy makes dynamic ordering suitable for high-traffic applications.

  4. Extend the Country object using entry points

    main

    You can add new attributes to the Country object from external Python packages using entry points. The entry point name becomes the attribute name on the Country instance, and its value is the return value of the registered function (which receives the Country instance as its only argument).

    To implement this, register an entry point under the group django_countries.Country in your package's installation configuration.

  5. How django-countries handles ISO 3166-1 name formatting

    main

    The library follows the ISO 3166-1 standard for country names, which includes specific punctuation patterns for sorting and qualifiers:

    • Parentheses ( ): Used for qualifiers, state designations, or articles (e.g., Bolivia (Plurinational State of) or Gambia (the)). These are not used for alphabetical inversion.
    • Commas , : Used to indicate an inversion for alphabetical sorting, where the distinctive word appears first (e.g., Tanzania, the United Republic of).
    • Multi-territory lists: Commas are also used as literal separators for lists of territories (e.g., Saint Helena, Ascension and Tristan da Cunha).

    Note that django-countries automatically strips standalone (the) from names (e.g., Gambia (the) becomes Gambia), but preserves (the) when it is part of a longer phrase or when it is capitalized (e.g., Bahamas (The)).

  6. Understand the Country Ordering Priority System

    main

    When determining the order of countries in a list, django-countries follows this priority hierarchy (from highest to lowest):

    1. Thread-local context: Set via countries_context(). Each option provided to the context manager overrides its corresponding global setting.
    2. Exact locale match: A key in COUNTRIES_FIRST_BY_LANGUAGE that matches the full locale (e.g., 'fr-CA'). This overrides COUNTRIES_FIRST.
    3. Auto-detect + base language: If COUNTRIES_FIRST_AUTO_DETECT = True, the detected country is prepended to the list chosen by the language match (e.g., fr-CA prepends CA to the fr group).
    4. Base language match: A key in COUNTRIES_FIRST_BY_LANGUAGE matching the base language (e.g., 'fr'). This overrides COUNTRIES_FIRST.
    5. Pure auto-detect: If COUNTRIES_FIRST_AUTO_DETECT = True and no language match is found, the detected country is prepended to COUNTRIES_FIRST.
    6. Static setting: The COUNTRIES_FIRST list.
    7. Alphabetical: The default fallback order.
  7. Understand ISO 3166-1 Country Name Formatting

    main

    The django-countries library follows ISO 3166-1 formatting conventions for country names. Understanding these patterns helps developers interpret why certain names appear with specific punctuation or capitalization:

    • Parentheses (): Used to mark qualifiers, articles, or alternate names (mirroring UNTERM standards). Example: Bahamas (The).
    • Commas ,: Used to indicate name inversions (for sorting purposes) or multi-territory enumerations.
    • Capitalization of "The": A capitalized "The" (e.g., Bahamas (The)) indicates an official self-designation. Other articles are typically lowercase following UNTERM normalization.
    • Sovereignty: ISO 3166-1 is a coding standard for data interchange, not a political definition; it includes territories regardless of their sovereignty status.

    Note that ISO 3166-1 Annex F allows discretion for inverting names for sorting, which is why formatting may vary between comma-separated and parenthesis-wrapped names.

  8. Input acceptance for CountryField in DRF

    main

    The CountryField serializer field is flexible with input. Regardless of your configured output format, it accepts:

    1. A country code string (e.g., "NZ").
    2. A verbose dictionary (e.g., {"code": "NZ", "name": "New Zealand"}).
    3. Country names, which will resolve to the correct code based on Django's active language (e.g., "Germany" or "Allemagne" in French will both resolve to "DE"). English names act as a fallback for all languages.
    # Both of these are valid input
    {"country": "NZ"}
    {"country": {"code": "NZ", "name": "New Zealand"}}
  9. Add changelog entries

    main

    Before releasing, you must add a changelog entry in the changes/ directory. Use the following format:

    • With issue/PR number: echo "Your change description" > changes/123.bugfix.rst
    • Without issue/PR (prefix with +): echo "Your change description" > changes/+20250104.misc.rst

    Supported types: feature, bugfix, doc, removal, misc.

    echo "Your change description" > changes/123.bugfix.rst
    echo "Your change description" > changes/+20250104.misc.rst
  10. Run code quality and linting checks

    main

    You can run all quality checks at once using just check. Alternatively, you can run individual tools directly using uv run.

    Individual Tool Commands

    • Formatting (import sorting + formatting):
      • Fix automatically: uv run ruff check --select I --fix django_countries
      • Format code: uv run ruff format django_countries
      • Check without changes: uv run ruff check --select I django_countries or uv run ruff format --check django_countries
    • Linting:
      • uv run ruff check django_countries
      • Security scan: uv run bandit -r django_countries -x tests
    • Type Checking:
      • uv run mypy django_countries
    just check
    uv run ruff check django_countries
    uv run mypy django_countries
  11. Use FilteredSelectMultiple in Django Admin for multiple countries

    main

    By default, multiple country fields render as a standard HTML multi-select dropdown. For a better user experience in the Django admin, you can use the FilteredSelectMultiple widget. This requires defining the field as a MultipleChoiceField in your form and overriding the field in the ModelAdmin.

    from django import forms
    from django.contrib import admin
    from django.contrib.admin.widgets import FilteredSelectMultiple
    from django_countries import countries
    from myapp.models import Incident
    
    class IncidentForm(forms.ModelForm):
        class Meta:
            model = Incident
            fields = '__all__'
    
        countries = forms.MultipleChoiceField(
            choices=list(countries),
            widget=FilteredSelectMultiple("Countries", is_stacked=False),
            required=False,
        )
    
    @admin.register(Incident)
    class IncidentAdmin(admin.ModelAdmin):
        form = IncidentForm
  12. Configure Django Admin for CountryField

    main

    Basic Admin Integration

    CountryField works automatically in the Django admin with a searchable dropdown. You can include it in list_display and search_fields (which supports searching by both code and name).

    from django.contrib import admin
    from myapp.models import Person
    
    @admin.register(Person)
    class PersonAdmin(admin.ModelAdmin):
        list_display = ['name', 'country']
        search_fields = ['name', 'country']