UPDATE (Django >=4.0)
Using pytz.country_names was convenient in older Django versions, as described below, but Django >=4.0 uses zoneinfo instead of pytz. Details in the release notes and here.
Unfortunately, zoneinfo does not appear to offer an equivalent of pytz.country_names (as far as I know).
There is, however, an up-to-date list of ISO 3166 country names in Python's (first-party) tzdata package: iso3166.tab
Note that tzdata is already installed if you're using Django 4+ on Windows.
For illustrative purposes only, here's a quick & dirty way to parse the iso3166.tab file into a dict similar to pytz.country_names (Python 3.9+):
from importlib import resources
with resources.files('tzdata.zoneinfo').joinpath('iso3166.tab').open('r') as f:
country_names = dict(
line.rstrip('\n').split('\t', 1)
for line in f
if not line.startswith('#')
)
Note, for Django's field choices, it would be easier to create a list directly, instead of a dict.
ORIGINAL (Django <4.0)
Django (<4.0) uses pytz (see e.g. django.utils.timezone), and pytz exposes a country_names dictionary, based on ISO 3166 (see pytz docs).
This pytz.country_names dictionary can be used to set your model field choices, or form field choices.
This might not cover all edge cases, but, at least, it does not add another obscure external dependency to your Django project.
Example for a model field (import pytz first):
country = models.CharField(max_length=2, choices=pytz.country_names.items())
Note that the dict keys (country codes) are all capitals.
One other thing to keep in mind, in case pytz is updated: as mentioned in the Django Model field reference
A new migration is created each time the order of choices changes.