def test_name_clash(self): backup_create = TenantModel.auto_create_schema TenantModel.auto_create_schema = False # public TenantModel.objects.create(schema_name="public") errors = check_schema_names(self.app_config) expected_errors = [ checks.Critical( "Name clash found between static and dynamic tenants: {'public'}", id="pgschemas.W004"), ] self.assertEqual(errors, expected_errors) TenantModel.objects.all().delete() # www TenantModel.objects.create(schema_name="www") errors = check_schema_names(self.app_config) expected_errors = [ checks.Critical( "Name clash found between static and dynamic tenants: {'www'}", id="pgschemas.W004"), ] self.assertEqual(errors, expected_errors) TenantModel.objects.all().delete() # sample TenantModel.objects.create(schema_name="sample") errors = check_schema_names(self.app_config) expected_errors = [ checks.Critical( "Name clash found between static and dynamic tenants: {'sample'}", id="pgschemas.W004"), ] self.assertEqual(errors, expected_errors) TenantModel.objects.all().delete() TenantModel.auto_create_schema = backup_create
def test_is_anonymous_authenticated_methods(self): """ <User Model>.is_anonymous/is_authenticated must not be methods. """ class BadUser(AbstractBaseUser): username = models.CharField(max_length=30, unique=True) USERNAME_FIELD = 'username' def is_anonymous(self): return True def is_authenticated(self): return True errors = checks.run_checks(app_configs=self.apps.get_app_configs()) self.assertEqual(errors, [ checks.Critical( '%s.is_anonymous must be an attribute or property rather than ' 'a method. Ignoring this is a security issue as anonymous ' 'users will be treated as authenticated!' % BadUser, obj=BadUser, id='auth.C009', ), checks.Critical( '%s.is_authenticated must be an attribute or property rather ' 'than a method. Ignoring this is a security issue as anonymous ' 'users will be treated as authenticated!' % BadUser, obj=BadUser, id='auth.C010', ), ])
def check_webhook_validation(app_configs=None, **kwargs): """ Check that DJSTRIPE_WEBHOOK_VALIDATION is valid """ from . import settings as djstripe_settings messages = [] validation_options = ("verify_signature", "retrieve_event") if djstripe_settings.WEBHOOK_VALIDATION is None: messages.append( checks.Warning( "Webhook validation is disabled, this is a security risk if the webhook view is enabled", hint="Set DJSTRIPE_WEBHOOK_VALIDATION to one of {}".format( ", ".join(validation_options)), id="djstripe.W004")) elif djstripe_settings.WEBHOOK_VALIDATION == "verify_signature": if not djstripe_settings.WEBHOOK_SECRET: messages.append( checks.Critical( "DJSTRIPE_WEBHOOK_VALIDATION='verify_signature' but DJSTRIPE_WEBHOOK_SECRET is not set", hint= "Set DJSTRIPE_WEBHOOK_SECRET or set DJSTRIPE_WEBHOOK_VALIDATION='retrieve_event'", id="djstripe.C006")) elif djstripe_settings.WEBHOOK_VALIDATION not in validation_options: messages.append( checks.Critical( "DJSTRIPE_WEBHOOK_VALIDATION is invalid", hint="Set DJSTRIPE_WEBHOOK_VALIDATION to one of {} or None". format(", ".join(validation_options)), id="djstripe.C007")) return messages
def check_stripe_api_key(app_configs=None, **kwargs): """Check the user has configured API live/test keys correctly.""" from . import settings as djstripe_settings messages = [] if not djstripe_settings.STRIPE_SECRET_KEY: msg = "Could not find a Stripe API key." hint = "Add STRIPE_TEST_SECRET_KEY and STRIPE_LIVE_SECRET_KEY to your settings." messages.append(checks.Critical(msg, hint=hint, id="djstripe.C001")) elif djstripe_settings.STRIPE_LIVE_MODE: if not djstripe_settings.LIVE_API_KEY.startswith( ("sk_live_", "rk_live_")): msg = "Bad Stripe live API key." hint = 'STRIPE_LIVE_SECRET_KEY should start with "sk_live_"' messages.append(checks.Critical(msg, hint=hint, id="djstripe.C002")) else: if not djstripe_settings.TEST_API_KEY.startswith( ("sk_test_", "rk_test_")): msg = "Bad Stripe test API key." hint = 'STRIPE_TEST_SECRET_KEY should start with "sk_test_"' messages.append(checks.Critical(msg, hint=hint, id="djstripe.C003")) return messages
def check_pootle_fs_working_dir(app_configs=None, **kwargs): import os from django.conf import settings missing_setting_error = checks.Critical( _("POOTLE_FS_WORKING_PATH setting is not set."), id="pootle.C019", ) missing_directory_error = checks.Critical( _("Path ('%s') pointed to by POOTLE_FS_WORKING_PATH doesn't exist." % settings.POOTLE_FS_WORKING_PATH), hint=_("Create the directory pointed to by `POOTLE_FS_WORKING_PATH`, " "or change the setting."), id="pootle.C020", ) not_writable_directory_error = checks.Critical( _("Path ('%s') pointed to by POOTLE_FS_WORKING_PATH is not writable by " "Pootle." % settings.POOTLE_FS_WORKING_PATH), hint=_("Add the write permission to the `POOTLE_FS_WORKING_PATH` " "or change the setting."), id="pootle.C021", ) errors = [] if not settings.POOTLE_FS_WORKING_PATH: errors.append(missing_setting_error) elif not os.path.exists(settings.POOTLE_FS_WORKING_PATH): errors.append(missing_directory_error) elif not os.access(settings.POOTLE_FS_WORKING_PATH, os.W_OK): errors.append(not_writable_directory_error) return errors
def check_library_versions(app_configs=None, **kwargs): from django import VERSION as django_version from lxml.etree import LXML_VERSION as lxml_version from translate.__version__ import ver as ttk_version errors = [] if django_version < DJANGO_MINIMUM_REQUIRED_VERSION: errors.append(checks.Critical( _("Your version of Django is too old."), hint=_("Try pip install --upgrade 'Django==%s'", _version_to_string(DJANGO_MINIMUM_REQUIRED_VERSION)), id="pootle.C002", )) if lxml_version < LXML_MINIMUM_REQUIRED_VERSION: errors.append(checks.Warning( _("Your version of lxml is too old."), hint=_("Try pip install --upgrade lxml"), id="pootle.W003", )) if ttk_version < TTK_MINIMUM_REQUIRED_VERSION: errors.append(checks.Critical( _("Your version of Translate Toolkit is too old."), hint=_("Try pip install --upgrade translate-toolkit"), id="pootle.C003", )) return errors
def check_id_submission_checkers(app_configs, **kwargs): # if already_ran(): return [] # errors = [] for checker_path in settings.IDSUBMIT_CHECKER_CLASSES: try: checker_class = import_string(checker_path) except Exception as e: errors.append( checks.Critical( "An exception was raised when trying to import the draft submission" "checker class '%s':\n %s" % (checker_path, e), hint= "Please check that the class exists and can be imported.", id="datatracker.E0008", )) try: checker = checker_class() except Exception as e: errors.append( checks.Critical( "An exception was raised when trying to instantiate the draft submission" "checker class '%s': %s" % (checker_path, e), hint="Please check that the class can be instantiated.", id="datatracker.E0009", )) continue for attr in ('name', ): if not hasattr(checker, attr): errors.append( checks.Critical( "The draft submission checker '%s' has no attribute '%s', which is required" % (checker_path, attr), hint="Please update the class.", id="datatracker.E0010", )) checker_methods = ( "check_file_txt", "check_file_xml", "check_fragment_txt", "check_fragment_xml", ) for method in checker_methods: if hasattr(checker, method): break else: errors.append( checks.Critical( "The draft submission checker '%s' has no recognised checker method; " "should be one or more of %s." % (checker_path, checker_methods), hint="Please update the class.", id="datatracker.E0011", )) return errors
def check_unsupported_python(app_configs=None, **kwargs): errors = [] if sys.version_info >= (3, 0): errors.append(checks.Critical( _("Pootle does not yet support Python 3."), hint=_("Use a Python 2.7 virtualenv."), id="pootle.C023", )) if sys.version_info < (2, 7): errors.append(checks.Critical( _("Pootle no longer supports Python versions older than 2.7"), hint=_("Use a Python 2.7 virtualenv."), id="pootle.C024", )) return errors
def check_canonical_url(app_configs=None, **kwargs): from django.conf import settings from django.contrib.sites.models import Site errors = [] no_canonical_error = checks.Critical( _("No canonical URL provided and default site set to example.com."), hint=_("Set the `POOTLE_CANONICAL_URL` in settings or update the " "default site if you are using django.contrib.sites."), id="pootle.C018") localhost_canonical_warning = checks.Warning( _("Canonical URL is set to http://localhost."), hint=_( "Set the `POOTLE_CANONICAL_URL` to an appropriate value for your " "site or leave it empty if you are using `django.contrib.sites`."), id="pootle.W020") try: contrib_site = Site.objects.get_current() except (ProgrammingError, OperationalError): if "django.contrib.sites" in settings.INSTALLED_APPS: return [] contrib_site = None uses_sites = (not settings.POOTLE_CANONICAL_URL and contrib_site) if uses_sites: site = Site.objects.get_current() if site.domain == "example.com": errors.append(no_canonical_error) elif not settings.POOTLE_CANONICAL_URL: errors.append(no_canonical_error) elif settings.POOTLE_CANONICAL_URL == "http://localhost": errors.append(localhost_canonical_warning) return errors
def check_id_submission_directories(app_configs, **kwargs): # if already_ran(): return [] # errors = [] for s in ( "IDSUBMIT_STAGING_PATH", "IDSUBMIT_REPOSITORY_PATH", "INTERNET_DRAFT_ARCHIVE_DIR", ): p = getattr(settings, s) if not os.path.exists(p): errors.append( checks.Critical( "A directory used by the ID submission tool does not exist at the path given\n" "in the settings file. The setting is:\n" " %s = %s" % (s, p), hint= ("Please either update the local settings to point at the correct directory," "or if the setting is correct, create the indicated directory." ), id="datatracker.E0006", )) return errors
def check_redis(app_configs=None, **kwargs): from django_rq.queues import get_queue from django_rq.workers import Worker errors = [] try: queue = get_queue() Worker.all(queue.connection) except Exception as e: conn_settings = queue.connection.connection_pool.connection_kwargs errors.append( checks.Critical( _("Could not connect to Redis (%s)" % e), hint=_("Make sure Redis is running on %(host)s:%(port)s" % conn_settings), id="pootle.C001", )) else: if len(queue.connection.smembers(Worker.redis_workers_keys)) == 0: # We need to check we're not running manage.py rqworker right now.. import sys if len(sys.argv) > 1 and sys.argv[1] in RQWORKER_WHITELIST: errors.append( checks.Warning( _("No RQ Worker running."), hint=_("Run new workers with manage.py rqworker"), id="pootle.W001", )) return errors
def maybe_create_svn_symlinks(settings): site_packages_dir = None errors = [] for p in sys.path: if ('/env/' in p or '/venv/' in p) and '/site-packages' in p: site_packages_dir = p break if site_packages_dir: for path in settings.SVN_PACKAGES: if os.path.exists(path): dir, name = os.path.split(path) package_link = os.path.join(site_packages_dir, name) if not os.path.lexists(package_link): os.symlink(path, package_link) else: errors.append( checks.Critical( "The setting SVN_PACKAGES specify a library path which\n" "does not exist:\n" " %s\n" % path, hint= "Please provide the correct python system site-package paths for\n" "svn and libsvn in SVN_PACKAGES.", id="datatracker.E0015", )) return errors
def check_stripe_api_version(app_configs=None, **kwargs): """Check the user has configured API version correctly.""" from . import settings as djstripe_settings messages = [] default_version = djstripe_settings.DEFAULT_STRIPE_API_VERSION version = djstripe_settings.get_stripe_api_version() if not validate_stripe_api_version(version): msg = "Invalid Stripe API version: {}".format(version) hint = "STRIPE_API_VERSION should be formatted as: YYYY-MM-DD" messages.append(checks.Critical(msg, hint=hint, id="djstripe.C004")) if version != default_version: msg = ( "The Stripe API version has a non-default value of '{}'. " "Non-default versions are not explicitly supported, and may " "cause compatibility issues.".format(version) ) hint = "Use the dj-stripe default for Stripe API version: {}".format( default_version ) messages.append(checks.Warning(msg, hint=hint, id="djstripe.W001")) return messages
def check_yang_model_directories(app_configs, **kwargs): # if already_ran(): return [] # errors = [] for s in ( "SUBMIT_YANG_RFC_MODEL_DIR", "SUBMIT_YANG_DRAFT_MODEL_DIR", "SUBMIT_YANG_INVAL_MODEL_DIR", ): p = getattr(settings, s) if not os.path.exists(p): errors.append( checks.Critical( "A directory used by the yang validation tools does not exist at the path\n" "gvien in the settings file. The setting is:\n" " %s = %s" % (s, p), hint= ("Please either update your local settings to point at the correct directory," "or if the setting is correct, create the indicated directory." ), id="datatracker.E0006", )) return errors
def check_native_jsonfield_postgres_engine(app_configs=None, **kwargs): """ Check that the DJSTRIPE_USE_NATIVE_JSONFIELD isn't set unless Postgres is in use. """ from . import settings as djstripe_settings messages = [] error_msg = "DJSTRIPE_USE_NATIVE_JSONFIELD is not compatible with engine {engine} for database {name}" if djstripe_settings.USE_NATIVE_JSONFIELD: for db_name, db_config in settings.DATABASES.items(): # Hi there. # You may be reading this because you are using Postgres, but # dj-stripe is not detecting that correctly. For example, maybe you # are using multiple databases with different engines, or you have # your own backend. As long as you are certain you can support jsonb, # you can use the SILENCED_SYSTEM_CHECKS setting to ignore this check. engine = db_config.get("ENGINE", "") if "postgresql" not in engine and "postgis" not in engine: messages.append(checks.Critical( error_msg.format(name=repr(db_name), engine=repr(engine)), hint="Switch to Postgres, or unset DJSTRIPE_USE_NATIVE_JSONFIELD", id="djstripe.C005" )) return messages
def check_revision(app_configs=None, **kwargs): from redis.exceptions import ConnectionError from pootle.core.models import Revision from pootle_store.models import Unit errors = [] try: revision = Revision.get() except (ConnectionError): return errors try: max_revision = Unit.max_revision() except (OperationalError, ProgrammingError): return errors if revision is None or revision < max_revision: errors.append( checks.Critical( _("Revision is missing or has an incorrect value."), hint=_( "Run `revision --restore` to reset the revision counter."), id="pootle.C016", )) return errors
def check(app_configs, **kwargs): errors = [] # Check for old hvad settings in global namespace for key in dir(djsettings): if key.startswith('HVAD_'): errors.append( checks.Critical( 'HVAD setting in global namespace', hint='HVAD settings are now namespaced in the HVAD dict.', obj=key, id='hvad.settings.C01', )) hvad_settings = getattr(djsettings, 'HVAD', {}) for key, value in hvad_settings.items(): try: checker = getattr(HvadSettingsChecks, 'check_%s' % key) except AttributeError: errors.append( checks.Warning('Unknown setting HVAD[%r]' % key, obj=key, id='hvad.settings.W01')) else: errors.extend(checker(value)) return errors
def check_redis(app_configs=None, **kwargs): from django_rq.queues import get_queue from django_rq.workers import Worker errors = [] try: queue = get_queue() Worker.all(queue.connection) except Exception as e: conn_settings = queue.connection.connection_pool.connection_kwargs errors.append( checks.Critical( _("Could not connect to Redis (%s)", e), hint=_("Make sure Redis is running on " "%(host)s:%(port)s") % conn_settings, id="pootle.C001", )) else: redis_version = tuple( int(x) for x in (queue.connection.info()["redis_version"].split("."))) if redis_version < REDIS_MINIMUM_REQUIRED_VERSION: errors.append( checks.Critical( _("Your version of Redis is too old."), hint=_( "Update your system's Redis server package to at least " "version %s", str(REDIS_MINIMUM_REQUIRED_VERSION)), id="pootle.C007", )) if len(queue.connection.smembers(Worker.redis_workers_keys)) == 0: # If we're not running 'pootle rqworker' report for whitelisted # commands import sys if len(sys.argv) > 1 and sys.argv[1] in RQWORKER_WHITELIST: errors.append( checks.Warning( # Translators: a worker processes background tasks _("No worker running."), # Translators: a worker processes background tasks hint=_("Run new workers with 'pootle rqworker'"), id="pootle.W001", )) return errors
def check_paypal_api_key(app_configs=None, **kwargs): """Check that the Paypal API keys are configured correctly""" messages = [] mode = getattr(djpaypal_settings, "PAYPAL_MODE", None) if mode not in VALID_MODES: msg = "Invalid PAYPAL_MODE specified: {}.".format(repr(mode)) hint = "PAYPAL_MODE must be one of {}".format(", ".join(repr(k) for k in VALID_MODES)) messages.append(checks.Critical(msg, hint=hint, id="djpaypal.C001")) for setting in "PAYPAL_CLIENT_ID", "PAYPAL_CLIENT_SECRET": if not getattr(djpaypal_settings, setting, None): msg = "Invalid value specified for {}".format(setting) hint = "Add PAYPAL_CLIENT_ID and PAYPAL_CLIENT_SECRET to your settings." messages.append(checks.Critical(msg, hint=hint, id="djpaypal.C002")) return messages
def check_redis(app_configs=None, **kwargs): from django_rq.queues import get_queue from django_rq.workers import Worker errors = [] try: queue = get_queue() workers = Worker.all(queue.connection) except Exception as e: conn_settings = queue.connection.connection_pool.connection_kwargs errors.append( checks.Critical( _("Could not connect to Redis (%s)") % (e), hint=_("Make sure Redis is running on %(host)s:%(port)s") % (conn_settings), id="pootle.C001", )) else: redis_version = tuple( int(x) for x in (queue.connection.info()["redis_version"].split("."))) if redis_version < REDIS_MINIMUM_REQUIRED_VERSION: errors.append( checks.Critical( _("Your version of Redis is too old."), hint=_( "Update your system's Redis server package to at least " "version %s" % str(REDIS_MINIMUM_REQUIRED_VERSION)), id="pootle.C007", )) if len(queue.connection.smembers(Worker.redis_workers_keys)) == 0: # We need to check we're not running manage.py rqworker right now.. import sys if len(sys.argv) > 1 and sys.argv[1] in RQWORKER_WHITELIST: errors.append( checks.Warning( _("No RQ Worker running."), hint=_("Run new workers with manage.py rqworker"), id="pootle.W001", )) return errors
def test_hvad_setting_namespace_error(self): with self.settings(HVAD_SOMETHING='foo', HVAD_OTHERTHING='bar'): errors = settings.check(apps) for key in 'HVAD_SOMETHING', 'HVAD_OTHERTHING': self.assertIn( checks.Critical( 'HVAD setting in global namespace', hint='HVAD settings are now namespaced in the HVAD dict.', obj=key, id='hvad.settings.C01'), errors)
def check_db_transaction_hooks(app_configs=None, **kwargs): from django.conf import settings errors = [] if settings.DATABASES['default']['ENGINE'].startswith("transaction_hooks"): errors.append(checks.Critical( _("Database connection uses transaction_hooks."), hint=_("Set the DATABASES['default']['ENGINE'] to use a Django " "backend from django.db.backends."), id="pootle.C006", )) return errors
def check_db_transaction_on_commit(app_configs=None, **kwargs): from django.db import connection errors = [] try: connection.on_commit except AttributeError: errors.append(checks.Critical( _("Database connection does not implement on_commit."), hint=_("Set the DATABASES['default']['ENGINE'] to use a backend " "from transaction_hooks.backends."), id="pootle.C006", )) return errors
def check_deprecated_settings(app_configs=None, **kwargs): errors = [] for old, new, dep_ver, remove_ver in DEPRECATIONS: # Old setting just disappeared, we just want you to cleanup if hasattr(settings, old) and new is None: errors.append( checks.Info( ("Setting %s was removed in Pootle %s." % (old, dep_ver)), hint=("Remove %s from your settings." % old), id="pootle.I002", )) continue # Both old and new defined, we'd like you to remove the old setting if hasattr(settings, old) and hasattr(settings, new): errors.append( checks.Info( ("Setting %s was replaced by %s in Pootle %s. Both are set." % (old, new, dep_ver)), hint=("Remove %s from your settings." % old), id="pootle.I002", )) continue # Old setting is present and new setting is not defined: # - Warn and copy # - Fail hard if its too old if hasattr(settings, old) and not hasattr(settings, new): from pootle import VERSION if VERSION >= tuple(int(x) for x in remove_ver.split(".")): errors.append( checks.Critical( ("Setting %s is deprecated and was removed in Pootle %s." % (old, remove_ver)), hint=("Use %s instead." % new), id="pootle.W002", )) else: errors.append( checks.Warning( ("Setting %s is deprecated and will be removed in " "Pootle %s." % (old, remove_ver)), hint=("Use %s instead." % new), id="pootle.W002", )) setattr(settings, new, getattr(settings, old)) continue return errors
def check_middleware(app_configs, **kwargs): errors = [] middle = 'django_auth_policy.middleware.AuthenticationPolicyMiddleware' m_classes = tuple(settings.MIDDLEWARE_CLASSES) if middle not in m_classes: errors.append( checks.Critical( msg=('AuthenticationPolicyMiddleware is missing'), hint=('Add {} to MIDDLEWARE_CLASSES'.format(middle)), id='django_auth_policy.C001', )) return errors
def check_mysql_timezones(app_configs=None, **kwargs): from django.db import connection missing_mysql_timezone_tables = checks.Critical( _("MySQL requires time zone settings."), hint=("Load the time zone tables " "http://dev.mysql.com/doc/refman/5.7/en/mysql-tzinfo-to-sql.html"), id="pootle.C022", ) errors = [] with connection.cursor() as cursor: if hasattr(cursor.db, "mysql_version"): cursor.execute("SELECT CONVERT_TZ(NOW(), 'UTC', 'UTC');") converted_now = cursor.fetchone()[0] if converted_now is None: errors.append(missing_mysql_timezone_tables) return errors
def check_proceedings_directories(app_configs, **kwargs): # if already_ran(): return [] # errors = [] for s in ("AGENDA_PATH", ): p = getattr(settings, s) if not os.path.exists(p): errors.append( checks.Critical( "A directory used for meeting materials does not exist at the path given\n" "in the settings file. The setting is:\n" " %s = %s" % (s, p), hint= ("Please either update the local settings to point at the correct directory," "or if the setting is correct, create the indicated directory." ), id="datatracker.E0013", )) return errors
def check_id_submission_files(app_configs, **kwargs): # if already_ran(): return [] # errors = [] for s in ("IDSUBMIT_IDNITS_BINARY", ): p = getattr(settings, s) if not os.path.exists(p): errors.append( checks.Critical( "A file used by the ID submission tool does not exist at the path given\n" "in the settings file. The setting is:\n" " %s = %s" % (s, p), hint= ("Please either update the local settings to point at the correct file," "or if the setting is correct, make sure the file is in place and has the right permissions." ), id="datatracker.E0007", )) return errors
def check_schema_names(app_configs, **kwargs): errors = [] static_names = set(settings.TENANTS.keys()) clone_reference = get_clone_reference() if clone_reference: static_names.add(clone_reference) try: dynamic_names = set(get_tenant_model().objects.values_list( "schema_name", flat=True)) except ProgrammingError: # This happens on the first run of migrate, with empty database. # It can also happen when the tenant model contains unapplied migrations that break. dynamic_names = set() intersection = static_names & dynamic_names if intersection: errors.append( checks.Critical( "Name clash found between static and dynamic tenants: %s" % intersection, id="pgschemas.W004", )) return errors
def check_native_jsonfield_postgres_engine(app_configs=None, **kwargs): """ Check that the DJSTRIPE_USE_NATIVE_JSONFIELD isn't set unless Postgres is in use. Only used on Django < 3.1. """ from .settings import djstripe_settings messages = [] error_msg = ( "DJSTRIPE_USE_NATIVE_JSONFIELD is not compatible with engine {engine} " "for database {name}" ) # This error check is skipped on Django 3.1+, because the native JSONField # will be used, which is compatible with mysql and sqlite. # https://docs.djangoproject.com/en/dev/releases/3.1/#postgresql-jsonfield if django.VERSION >= (3, 1): return messages if djstripe_settings.USE_NATIVE_JSONFIELD: for db_name, db_config in settings.DATABASES.items(): # Hi there. # You may be reading this because you are using Postgres, but # dj-stripe is not detecting that correctly. For example, maybe you # are using multiple databases with different engines, or you have # your own backend. As long as you are certain you can support jsonb, # you can use the SILENCED_SYSTEM_CHECKS setting to ignore this check. engine = db_config.get("ENGINE", "") if "postgresql" not in engine and "postgis" not in engine: messages.append( checks.Critical( error_msg.format(name=repr(db_name), engine=repr(engine)), hint="Switch to Postgres, or unset " "DJSTRIPE_USE_NATIVE_JSONFIELD", id="djstripe.C005", ) ) return messages