Django: Disallow Postgres-specific fields

Django has some fields you can define in your models that are specific to a given database engine. The problem is, once your codebase starts using those fields, the project will never work again with another database engine because of the migrations that will fail on different engines.

However, it’s hard to remind yourself not to use these fields, and coordinating that on a big project with multiple developers is close to impossible. Which is why you can write a Django system check that will disallow such fields from creating a migration file:

import inspect

import django.contrib.postgres.fields
import django.apps

from django.core.checks import Error, register


# noinspection PyUnusedLocal
@register()
def disallow_postgres_specific_fields_check(app_configs, **kwargs):
    errors = []
    disallowed_fields = [
        item[1]
        for item in inspect.getmembers(django.contrib.postgres.fields, inspect.isclass)
    ]

    for model in django.apps.apps.get_models():
        for field in model._meta.get_fields():
            for disallowed_field in disallowed_fields:
                # PyCharm bug, see: https://youtrack.jetbrains.com/issue/PY-32860
                # noinspection PyTypeHints
                if isinstance(field, disallowed_field):
                    errors.append(
                        Error(
                            f"Field {field} cannot be used as it is a Postgres-specific field: "
                            f"{disallowed_field.__name__}",
                            hint="Use fields that are database engine-agnostic and provided by Django.",
                            id="config.E005", # Use an ID specific to your codebase
                        )
                    )

    return errors

Here’s the final commit in the shipper project, if you’re interested.

Related Posts

comments