def _check_mysql_version(self): errors = [] any_conn_works = False for _alias, conn in mysql_connections(): if (hasattr(conn, "mysql_version") and not connection_is_mariadb(conn) and conn.mysql_version >= (5, 7)): any_conn_works = True if not any_conn_works: errors.append( checks.Error( "MySQL 5.7+ is required to use JSONField", hint="At least one of your DB connections should be to " "MySQL 5.7+", obj=self, id="django_mysql.E016", )) return errors
def test_check_unique_sf_field_names(self): class MyModel(hc_models.HerokuConnectModel): sf_object_name = "My_Object__c" date1 = hc_models.DateTime(sf_field_name="Date1__c", db_column="date1") date2 = hc_models.DateTime(sf_field_name="Date1__c", db_column="date2") class Meta: app_label = "test" abstract = True errors = MyModel.check() assert errors == [ checks.Error( "test.MyModel has duplicate Salesforce field names.", hint=["Date1__c"], id="heroku_connect.E003", ) ]
def test_content_type_field_pointing_to_wrong_model(self): class Model(models.Model): content_type = models.ForeignKey('self', models.CASCADE) # should point to ContentType object_id = models.PositiveIntegerField() content_object = GenericForeignKey( 'content_type', 'object_id') errors = Model.content_object.check() expected = [ checks.Error( "'Model.content_type' is not a ForeignKey to 'contenttypes.ContentType'.", hint=( "GenericForeignKeys must use a ForeignKey to " "'contenttypes.ContentType' as the 'content_type' field." ), obj=Model.content_object, id='contenttypes.E004', ) ] self.assertEqual(errors, expected)
def _check_mariadb_version(self): errors = [] any_conn_works = False conn_names = ['default'] + list(set(connections) - {'default'}) for db in conn_names: conn = connections[db] if (hasattr(conn, 'mysql_version') and connection_is_mariadb(conn) and conn.mysql_version >= (10, 0, 1)): any_conn_works = True if not any_conn_works: errors.append( checks.Error( 'MariaDB 10.0.1+ is required to use DynamicField', hint='At least one of your DB connections should be to ' 'MariaDB 10.0.1+', obj=self, id='django_mysql.E013')) return errors
def test_generic_inline_model_admin_bad_fk_field(self): "A GenericInlineModelAdmin raises problems if the ct_fk_field points to a non-existent field." class InfluenceInline(GenericStackedInline): model = Influence ct_fk_field = 'nonexistent' class SongAdmin(admin.ModelAdmin): inlines = [InfluenceInline] errors = SongAdmin.check(model=Song) expected = [ checks.Error( "'ct_fk_field' references 'nonexistent', which is not a field on 'admin_checks.Influence'.", hint=None, obj=InfluenceInline, id='admin.E303', ) ] self.assertEqual(errors, expected)
def test_required_fields_is_list(self): """REQUIRED_FIELDS should be a list.""" class CustomUserNonListRequiredFields(AbstractBaseUser): username = models.CharField(max_length=30, unique=True) date_of_birth = models.DateField() USERNAME_FIELD = "username" REQUIRED_FIELDS = "date_of_birth" errors = checks.run_checks(app_configs=self.apps.get_app_configs()) self.assertEqual( errors, [ checks.Error( "'REQUIRED_FIELDS' must be a list or tuple.", obj=CustomUserNonListRequiredFields, id="auth.E001", ), ], )
def _check_mysql_version(self): errors = [] any_conn_works = False for alias, conn in mysql_connections(): if (hasattr(conn, 'mysql_version') and not connection_is_mariadb(conn) and conn.mysql_version >= (5, 7)): any_conn_works = True if not any_conn_works: errors.append( checks.Error( 'MySQL 5.7+ is required to use JSONField', hint='At least one of your DB connections should be to ' 'MySQL 5.7+', obj=self, id='django_mysql.E016', ), ) return errors
def test_missing_generic_foreign_key(self): class TaggedItem(models.Model): content_type = models.ForeignKey(ContentType, models.CASCADE) object_id = models.PositiveIntegerField() class Bookmark(models.Model): tags = GenericRelation("TaggedItem") self.assertEqual( Bookmark.tags.field.check(), [ checks.Error( "The GenericRelation defines a relation with the model " "'contenttypes_tests.TaggedItem', but that model does not have a " "GenericForeignKey.", obj=Bookmark.tags.field, id="contenttypes.E004", ) ], )
def _check_object_id_field(self): target = self.rel.to if isinstance(target, ModelBase): opts = target._meta try: opts.get_field(self.object_id_field_name) except FieldDoesNotExist: return [ checks.Error( 'The field refers to %s.%s field which is missing.' % (opts.object_name, self.object_id_field_name), hint=None, obj=self, id='contenttypes.E003', ) ] else: return [] else: return []
def test_missing_generic_foreign_key(self): class TaggedItem(models.Model): content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() class Bookmark(models.Model): tags = GenericRelation('TaggedItem') errors = Bookmark.tags.field.check() expected = [ checks.Error( ("The GenericRelation defines a relation with the model " "'contenttypes_tests.TaggedItem', but that model does not have a " "GenericForeignKey."), hint=None, obj=Bookmark.tags.field, id='contenttypes.E004', ) ] self.assertEqual(errors, expected)
def test_generic_inline_model_admin_non_generic_model(self): """ A model without a GenericForeignKey raises problems if it's included in a GenericInlineModelAdmin definition. """ class BookInline(GenericStackedInline): model = Book class SongAdmin(admin.ModelAdmin): inlines = [BookInline] errors = SongAdmin(Song, AdminSite()).check() expected = [ checks.Error( "'admin_checks.Book' has no GenericForeignKey.", obj=BookInline, id='admin.E301', ) ] self.assertEqual(errors, expected)
def test_new_fields(self): class NoNewFields(Person): newfield = models.BooleanField() class Meta: proxy = True # don't register this model in the app_cache for the current app, # otherwise the check fails when other tests are being run. app_label = 'no_such_app' errors = NoNewFields.check() expected = [ checks.Error( "Proxy model 'NoNewFields' contains model fields.", hint=None, obj=None, id='models.E017', ) ] self.assertEqual(errors, expected)
def test_CommaSeparatedIntegerField_deprecated(self): class CommaSeparatedIntegerModel(models.Model): csi = models.CommaSeparatedIntegerField(max_length=64) model = CommaSeparatedIntegerModel() self.assertEqual( model.check(), [ checks.Error( "CommaSeparatedIntegerField is removed except for support in " "historical migrations.", hint= ("Use " "CharField(validators=[validate_comma_separated_integer_list]) " "instead."), obj=CommaSeparatedIntegerModel._meta.get_field("csi"), id="fields.E901", ) ], )
def test_user_specified_details(self): class MyField(models.Field): system_check_removed_details = { 'msg': 'Support for this field is gone.', 'hint': 'Use something else.', 'id': 'fields.E999', } class Model(models.Model): name = MyField() model = Model() self.assertEqual(model.check(), [ checks.Error( msg='Support for this field is gone.', hint='Use something else.', obj=Model._meta.get_field('name'), id='fields.E999', ) ])
def check_subscriber_key_length(app_configs=None, **kwargs): """ Check that DJSTRIPE_SUBSCRIBER_CUSTOMER_KEY fits in metadata. Docs: https://stripe.com/docs/api#metadata """ from . import settings as djstripe_settings messages = [] key = djstripe_settings.SUBSCRIBER_CUSTOMER_KEY key_size = len(str(key)) if key and key_size > 40: messages.append( checks.Error( "DJSTRIPE_SUBSCRIBER_CUSTOMER_KEY must be no more than 40 characters long", hint="Current value: %r (%i characters)" % (key, key_size), id="djstripe.E001")) return messages
def test_check_unique_sf_field_names(self): class MyModel(hc_models.HerokuConnectModel): sf_object_name = 'My_Object__c' date1 = hc_models.DateTime(sf_field_name='Date1__c', db_column='date1') date2 = hc_models.DateTime(sf_field_name='Date1__c', db_column='date2') class Meta: app_label = 'test' abstract = True errors = MyModel.check() assert errors == [ checks.Error( "test.MyModel has duplicate Salesforce field names.", hint=['Date1__c'], id='heroku_connect.E003', ) ]
def _check_default(self, **kwargs): try: default = self.get_default() self.enum(default) return [] except ValueError: return [ checks.Error( FieldErrors.E02.display.format( cls=self.__class__.__name__, default=default, enum=self.enum, ), obj=self, id=FieldErrors.E02, hint=_( 'Add an enum item with `{0!r}` as value, eg.: `UNDEFINED = {0!r}`, ' 'or inform a valid default value.').format(default), ) ]
def test_verbose_name_max_length(self): class Checked(models.Model): class Meta: verbose_name = ( "some ridiculously long verbose name that is out of control" * 5) errors = checks.run_checks(self.apps.get_app_configs()) self.assertEqual( errors, [ checks.Error( "The verbose_name of model 'auth_tests.Checked' must be at most " "244 characters for its builtin permission names to be at most 255 " "characters.", obj=Checked, id="auth.E007", ), ], )
def test_generic_inline_model_admin_non_gfk_fk_field(self): "A GenericInlineModelAdmin raises problems if the ct_fk_field points to a field that isn't part of a GenericForeignKey" class InfluenceInline(GenericStackedInline): model = Influence ct_fk_field = 'name' class SongAdmin(admin.ModelAdmin): inlines = [InfluenceInline] errors = SongAdmin.check(model=Song) expected = [ checks.Error( "'admin_checks.Influence' has no GenericForeignKey using content type field 'content_type' and object ID field 'name'.", hint=None, obj=InfluenceInline, id='admin.E304', ) ] self.assertEqual(errors, expected)
def _check_supported(self, databases): errors = [] for db in databases: if not router.allow_migrate_model(db, self.model): continue connection = connections[db] if (self.model._meta.required_db_vendor and self.model._meta.required_db_vendor != connection.vendor): continue if not ('supports_json_field' in self.model._meta.required_db_features or connection.features.supports_json_field): errors.append( checks.Error( '%s does not support JSONFields.' % connection.display_name, obj=self.model, id='fields.E180', )) return errors
def _check_exclude(self, obj): """ Check that exclude is a sequence without duplicates. """ if obj.exclude is None: # default value is None return [] elif not isinstance(obj.exclude, (list, tuple)): return must_be('a list or tuple', option='exclude', obj=obj, id='admin.E014') elif len(obj.exclude) > len(set(obj.exclude)): return [ checks.Error( "The value of 'exclude' contains duplicate field(s).", obj=obj.__class__, id='admin.E015', ) ] else: return []
def test_invalid_content_type_field(self): class Model(models.Model): content_type = models.IntegerField() # should be ForeignKey object_id = models.PositiveIntegerField() content_object = GenericForeignKey( 'content_type', 'object_id') errors = Model.content_object.check() expected = [ checks.Error( "'Model.content_type' is not a ForeignKey.", hint=( "GenericForeignKeys must use a ForeignKey to " "'contenttypes.ContentType' as the 'content_type' field." ), obj=Model.content_object, id='contenttypes.E003', ) ] self.assertEqual(errors, expected)
def test_readonly_and_editable(self): class SongAdmin(admin.ModelAdmin): readonly_fields = ["original_release"] list_display = ["pk", "original_release"] list_editable = ["original_release"] fieldsets = [ (None, { "fields": ["title", "original_release"], }), ] errors = SongAdmin(Song, AdminSite()).check() expected = [ checks.Error( "The value of 'list_editable[0]' refers to 'original_release', " "which is not editable through the admin.", obj=SongAdmin, id='admin.E125', ) ] self.assertEqual(errors, expected)
def _check_model(cls): errors = super()._check_model() _will_check_abstract_method = cls.__dict__.get( '_will_check_abstract_method', True) if _will_check_abstract_method and not cls._meta.abstract: model_functions = filter( callable, map(lambda attr: getattr(cls, attr, None), dir(cls))) abstract_functions = filter( lambda func: getattr(func, '__isabstractmethod__', False), model_functions) for abstract_function in abstract_functions: errors.append( checks.Error( "'%s' is abstracted but not implemented." % (abstract_function.__name__), obj=cls, )) return errors
def _check_generic_foreign_key_existence(self): target = self.remote_field.model if isinstance(target, ModelBase): fields = target._meta.private_fields if any( self._is_matching_generic_foreign_key(field) for field in fields): return [] else: return [ checks.Error( "The GenericRelation defines a relation with the model " "'%s.%s', but that model does not have a GenericForeignKey." % (target._meta.app_label, target._meta.object_name), obj=self, id='contenttypes.E004', ) ] else: return []
def test_username_not_in_required_fields(self, apps): """USERNAME_FIELD should not appear in REQUIRED_FIELDS.""" class CustomUserBadRequiredFields(AbstractBaseUser): username = models.CharField(max_length=30, unique=True) date_of_birth = models.DateField() USERNAME_FIELD = 'username' REQUIRED_FIELDS = ['username', 'date_of_birth'] errors = checks.run_checks(apps.get_app_configs()) expected = [ checks.Error( ("The field named as the 'USERNAME_FIELD' for a custom user model " "must not be included in 'REQUIRED_FIELDS'."), hint=None, obj=CustomUserBadRequiredFields, id='auth.E002', ), ] self.assertEqual(errors, expected)
def test_invalid_content_type_field(self): class Model(models.Model): content_type = models.IntegerField() # should be ForeignKey object_id = models.PositiveIntegerField() content_object = GenericForeignKey("content_type", "object_id") self.assertEqual( Model.content_object.check(), [ checks.Error( "'Model.content_type' is not a ForeignKey.", hint=( "GenericForeignKeys must use a ForeignKey to " "'contenttypes.ContentType' as the 'content_type' field." ), obj=Model.content_object, id="contenttypes.E003", ) ], )
def test_graceful_m2m_fail(self): """ Regression test for #12203/#12237 - Fail more gracefully when a M2M field that specifies the 'through' option is included in the 'fields' or the 'fieldsets' ModelAdmin options. """ class BookAdmin(admin.ModelAdmin): fields = ['authors'] errors = BookAdmin.check(model=Book) expected = [ checks.Error( ('"fields" cannot include the ManyToManyField "authors", ' 'because "authors" manually specifies relationship model.'), hint=None, obj=BookAdmin, id='admin.E013', ) ] self.assertEqual(errors, expected)
def test_username_partially_unique(self): class CustomUserPartiallyUnique(AbstractBaseUser): username = models.CharField(max_length=30) USERNAME_FIELD = "username" class Meta: constraints = [ UniqueConstraint( fields=["username"], name="partial_username_unique", condition=Q(password__isnull=False), ), ] errors = checks.run_checks(app_configs=self.apps.get_app_configs()) self.assertEqual( errors, [ checks.Error( "'CustomUserPartiallyUnique.username' must be unique because " "it is named as the 'USERNAME_FIELD'.", obj=CustomUserPartiallyUnique, id="auth.E003", ), ], ) with self.settings(AUTHENTICATION_BACKENDS=["my.custom.backend"]): errors = checks.run_checks(app_configs=self.apps.get_app_configs()) self.assertEqual( errors, [ checks.Warning( "'CustomUserPartiallyUnique.username' is named as the " "'USERNAME_FIELD', but it is not unique.", hint=("Ensure that your authentication backend(s) can " "handle non-unique usernames."), obj=CustomUserPartiallyUnique, id="auth.W004", ), ], )
def test_cannot_include_through(self): class FieldsetBookAdmin(admin.ModelAdmin): fieldsets = ( ('Header 1', { 'fields': ('name', ) }), ('Header 2', { 'fields': ('authors', ) }), ) errors = FieldsetBookAdmin(Book, AdminSite()).check() expected = [ checks.Error( "The value of 'fieldsets[1][1][\"fields\"]' cannot include the ManyToManyField " "'authors', because that field manually specifies a relationship model.", obj=FieldsetBookAdmin, id='admin.E013', ) ] self.assertEqual(errors, expected)