Refs #11964 -- Made constraint support check respect required_db_features.
This will notably silence the warnings issued when running the test suite on MySQL.
This commit is contained in:
parent
2fb872e56f
commit
8b3e1b6e9e
|
@ -1813,7 +1813,10 @@ class Model(metaclass=ModelBase):
|
|||
if not router.allow_migrate_model(db, cls):
|
||||
continue
|
||||
connection = connections[db]
|
||||
if connection.features.supports_table_check_constraints:
|
||||
if (
|
||||
connection.features.supports_table_check_constraints or
|
||||
'supports_table_check_constraints' in cls._meta.required_db_features
|
||||
):
|
||||
continue
|
||||
if any(isinstance(constraint, CheckConstraint) for constraint in cls._meta.constraints):
|
||||
errors.append(
|
||||
|
|
|
@ -2,12 +2,13 @@ from django.db import models
|
|||
|
||||
|
||||
class Product(models.Model):
|
||||
name = models.CharField(max_length=255)
|
||||
color = models.CharField(max_length=32, null=True)
|
||||
price = models.IntegerField(null=True)
|
||||
discounted_price = models.IntegerField(null=True)
|
||||
|
||||
class Meta:
|
||||
required_db_features = {
|
||||
'supports_table_check_constraints',
|
||||
}
|
||||
constraints = [
|
||||
models.CheckConstraint(
|
||||
check=models.Q(price__gt=models.F('discounted_price')),
|
||||
|
@ -17,6 +18,15 @@ class Product(models.Model):
|
|||
check=models.Q(price__gt=0),
|
||||
name='%(app_label)s_%(class)s_price_gt_0',
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
class UniqueConstraintProduct(models.Model):
|
||||
name = models.CharField(max_length=255)
|
||||
color = models.CharField(max_length=32, null=True)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(fields=['name', 'color'], name='name_color_uniq'),
|
||||
models.UniqueConstraint(
|
||||
fields=['name'],
|
||||
|
@ -31,6 +41,9 @@ class AbstractModel(models.Model):
|
|||
|
||||
class Meta:
|
||||
abstract = True
|
||||
required_db_features = {
|
||||
'supports_table_check_constraints',
|
||||
}
|
||||
constraints = [
|
||||
models.CheckConstraint(
|
||||
check=models.Q(age__gte=18),
|
||||
|
|
|
@ -3,7 +3,7 @@ from django.db import IntegrityError, connection, models
|
|||
from django.db.models.constraints import BaseConstraint
|
||||
from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature
|
||||
|
||||
from .models import ChildModel, Product
|
||||
from .models import ChildModel, Product, UniqueConstraintProduct
|
||||
|
||||
|
||||
def get_constraints(table):
|
||||
|
@ -69,9 +69,9 @@ class CheckConstraintTests(TestCase):
|
|||
|
||||
@skipUnlessDBFeature('supports_table_check_constraints')
|
||||
def test_database_constraint(self):
|
||||
Product.objects.create(name='Valid', price=10, discounted_price=5)
|
||||
Product.objects.create(price=10, discounted_price=5)
|
||||
with self.assertRaises(IntegrityError):
|
||||
Product.objects.create(name='Invalid', price=10, discounted_price=20)
|
||||
Product.objects.create(price=10, discounted_price=20)
|
||||
|
||||
@skipUnlessDBFeature('supports_table_check_constraints', 'can_introspect_check_constraints')
|
||||
def test_name(self):
|
||||
|
@ -92,9 +92,9 @@ class CheckConstraintTests(TestCase):
|
|||
class UniqueConstraintTests(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.p1, cls.p2 = Product.objects.bulk_create([
|
||||
Product(name='p1', color='red'),
|
||||
Product(name='p2'),
|
||||
cls.p1, cls.p2 = UniqueConstraintProduct.objects.bulk_create([
|
||||
UniqueConstraintProduct(name='p1', color='red'),
|
||||
UniqueConstraintProduct(name='p2'),
|
||||
])
|
||||
|
||||
def test_eq(self):
|
||||
|
@ -177,19 +177,20 @@ class UniqueConstraintTests(TestCase):
|
|||
|
||||
def test_database_constraint(self):
|
||||
with self.assertRaises(IntegrityError):
|
||||
Product.objects.create(name=self.p1.name, color=self.p1.color)
|
||||
UniqueConstraintProduct.objects.create(name=self.p1.name, color=self.p1.color)
|
||||
|
||||
def test_model_validation(self):
|
||||
with self.assertRaisesMessage(ValidationError, 'Product with this Name and Color already exists.'):
|
||||
Product(name=self.p1.name, color=self.p1.color).validate_unique()
|
||||
msg = 'Unique constraint product with this Name and Color already exists.'
|
||||
with self.assertRaisesMessage(ValidationError, msg):
|
||||
UniqueConstraintProduct(name=self.p1.name, color=self.p1.color).validate_unique()
|
||||
|
||||
def test_model_validation_with_condition(self):
|
||||
"""Partial unique constraints are ignored by Model.validate_unique()."""
|
||||
Product(name=self.p1.name, color='blue').validate_unique()
|
||||
Product(name=self.p2.name).validate_unique()
|
||||
UniqueConstraintProduct(name=self.p1.name, color='blue').validate_unique()
|
||||
UniqueConstraintProduct(name=self.p2.name).validate_unique()
|
||||
|
||||
def test_name(self):
|
||||
constraints = get_constraints(Product._meta.db_table)
|
||||
constraints = get_constraints(UniqueConstraintProduct._meta.db_table)
|
||||
expected_name = 'name_color_uniq'
|
||||
self.assertIn(expected_name, constraints)
|
||||
|
||||
|
|
|
@ -70,14 +70,24 @@ class Comment(models.Model):
|
|||
article = models.ForeignKey(Article, models.CASCADE, db_index=True)
|
||||
email = models.EmailField()
|
||||
pub_date = models.DateTimeField()
|
||||
up_votes = models.PositiveIntegerField()
|
||||
body = models.TextField()
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.CheckConstraint(name='up_votes_gte_0_check', check=models.Q(up_votes__gte=0)),
|
||||
models.UniqueConstraint(fields=['article', 'email', 'pub_date'], name='article_email_pub_date_uniq'),
|
||||
]
|
||||
indexes = [
|
||||
models.Index(fields=['email', 'pub_date'], name='email_pub_date_idx'),
|
||||
]
|
||||
|
||||
|
||||
class CheckConstraintModel(models.Model):
|
||||
up_votes = models.PositiveIntegerField()
|
||||
|
||||
class Meta:
|
||||
required_db_features = {
|
||||
'supports_table_check_constraints',
|
||||
}
|
||||
constraints = [
|
||||
models.CheckConstraint(name='up_votes_gte_0_check', check=models.Q(up_votes__gte=0)),
|
||||
]
|
||||
|
|
|
@ -6,7 +6,8 @@ from django.db.utils import DatabaseError
|
|||
from django.test import TransactionTestCase, skipUnlessDBFeature
|
||||
|
||||
from .models import (
|
||||
Article, ArticleReporter, City, Comment, Country, District, Reporter,
|
||||
Article, ArticleReporter, CheckConstraintModel, City, Comment, Country,
|
||||
District, Reporter,
|
||||
)
|
||||
|
||||
|
||||
|
@ -241,17 +242,20 @@ class IntrospectionTests(TransactionTestCase):
|
|||
self.assertEqual(details['check'], check)
|
||||
self.assertEqual(details['foreign_key'], foreign_key)
|
||||
|
||||
with connection.cursor() as cursor:
|
||||
constraints = connection.introspection.get_constraints(cursor, Comment._meta.db_table)
|
||||
# Test custom constraints
|
||||
custom_constraints = {
|
||||
'article_email_pub_date_uniq',
|
||||
'email_pub_date_idx',
|
||||
}
|
||||
with connection.cursor() as cursor:
|
||||
constraints = connection.introspection.get_constraints(cursor, Comment._meta.db_table)
|
||||
if (
|
||||
connection.features.supports_column_check_constraints and
|
||||
connection.features.can_introspect_check_constraints
|
||||
):
|
||||
constraints.update(
|
||||
connection.introspection.get_constraints(cursor, CheckConstraintModel._meta.db_table)
|
||||
)
|
||||
custom_constraints.add('up_votes_gte_0_check')
|
||||
assertDetails(constraints['up_votes_gte_0_check'], ['up_votes'], check=True)
|
||||
assertDetails(constraints['article_email_pub_date_uniq'], ['article_id', 'email', 'pub_date'], unique=True)
|
||||
|
|
|
@ -1191,3 +1191,13 @@ class ConstraintsTests(SimpleTestCase):
|
|||
)
|
||||
expected = [] if connection.features.supports_table_check_constraints else [warn, warn]
|
||||
self.assertCountEqual(errors, expected)
|
||||
|
||||
def test_check_constraints_required_db_features(self):
|
||||
class Model(models.Model):
|
||||
age = models.IntegerField()
|
||||
|
||||
class Meta:
|
||||
required_db_features = {'supports_table_check_constraints'}
|
||||
constraints = [models.CheckConstraint(check=models.Q(age__gte=18), name='is_adult')]
|
||||
|
||||
self.assertEqual(Model.check(), [])
|
||||
|
|
Loading…
Reference in New Issue