1
0
mirror of https://github.com/django/django.git synced 2024-12-22 17:16:24 +00:00

Refs #34944 -- Propagated system checks for GeneratedField.output_field.

This commit is contained in:
Mariusz Felisiak 2023-11-12 20:33:06 +01:00
parent 5875f03ce6
commit c705625ebf
4 changed files with 116 additions and 2 deletions

View File

@ -286,6 +286,8 @@ class Field(RegisterLookupMixin):
Check if field name is valid, i.e. 1) does not end with an
underscore, 2) does not contain "__" and 3) is not "pk".
"""
if self.name is None:
return []
if self.name.endswith("_"):
return [
checks.Error(

View File

@ -63,11 +63,45 @@ class GeneratedField(Field):
def check(self, **kwargs):
databases = kwargs.get("databases") or []
return [
errors = [
*super().check(**kwargs),
*self._check_supported(databases),
*self._check_persistence(databases),
]
output_field_clone = self.output_field.clone()
output_field_clone.model = self.model
output_field_checks = output_field_clone.check(databases=databases)
if output_field_checks:
separator = "\n "
error_messages = separator.join(
f"{output_check.msg} ({output_check.id})"
for output_check in output_field_checks
if isinstance(output_check, checks.Error)
)
if error_messages:
errors.append(
checks.Error(
"GeneratedField.output_field has errors:"
f"{separator}{error_messages}",
obj=self,
id="fields.E223",
)
)
warning_messages = separator.join(
f"{output_check.msg} ({output_check.id})"
for output_check in output_field_checks
if isinstance(output_check, checks.Warning)
)
if warning_messages:
errors.append(
checks.Warning(
"GeneratedField.output_field has warnings:"
f"{separator}{warning_messages}",
obj=self,
id="fields.W224",
)
)
return errors
def _check_supported(self, databases):
errors = []

View File

@ -213,6 +213,8 @@ Model fields
``GeneratedField``\s.
* **fields.E222**: ``<database>`` does not support persisted
``GeneratedField``\s.
* **fields.E223**: ``GeneratedField.output_field`` has errors: ...
* **fields.W224**: ``GeneratedField.output_field`` has warnings: ...
* **fields.E900**: ``IPAddressField`` has been removed except for support in
historical migrations.
* **fields.W900**: ``IPAddressField`` has been deprecated. Support for it

View File

@ -4,7 +4,7 @@ import uuid
from django.core.checks import Error
from django.core.checks import Warning as DjangoWarning
from django.db import connection, models
from django.db.models.functions import Coalesce, Pi
from django.db.models.functions import Coalesce, LPad, Pi
from django.test import SimpleTestCase, TestCase, skipIfDBFeature, skipUnlessDBFeature
from django.test.utils import isolate_apps, override_settings
from django.utils.functional import lazy
@ -1336,3 +1336,79 @@ class GeneratedFieldTests(TestCase):
Model._meta.get_field("field").check(databases={"default"}),
expected_errors,
)
@skipUnlessDBFeature("supports_stored_generated_columns")
def test_output_field_check_error(self):
class Model(models.Model):
value = models.DecimalField(max_digits=5, decimal_places=2)
field = models.GeneratedField(
expression=models.F("value") * 2,
output_field=models.DecimalField(max_digits=-1, decimal_places=-1),
db_persist=True,
)
expected_errors = [
Error(
"GeneratedField.output_field has errors:"
"\n 'decimal_places' must be a non-negative integer. (fields.E131)"
"\n 'max_digits' must be a positive integer. (fields.E133)",
obj=Model._meta.get_field("field"),
id="fields.E223",
),
]
self.assertEqual(
Model._meta.get_field("field").check(databases={"default"}),
expected_errors,
)
@skipUnlessDBFeature("supports_stored_generated_columns")
def test_output_field_charfield_unlimited_error(self):
class Model(models.Model):
name = models.CharField(max_length=255)
field = models.GeneratedField(
expression=LPad("name", 7, models.Value("xy")),
output_field=models.CharField(),
db_persist=True,
)
expected_errors = (
[]
if connection.features.supports_unlimited_charfield
else [
Error(
"GeneratedField.output_field has errors:"
"\n CharFields must define a 'max_length' attribute. "
"(fields.E120)",
obj=Model._meta.get_field("field"),
id="fields.E223",
),
]
)
self.assertEqual(
Model._meta.get_field("field").check(databases={"default"}),
expected_errors,
)
@skipUnlessDBFeature("supports_stored_generated_columns")
def test_output_field_check_warning(self):
class Model(models.Model):
value = models.IntegerField()
field = models.GeneratedField(
expression=models.F("value") * 2,
output_field=models.IntegerField(max_length=40),
db_persist=True,
)
expected_warnings = [
DjangoWarning(
"GeneratedField.output_field has warnings:"
"\n 'max_length' is ignored when used with IntegerField. "
"(fields.W122)",
obj=Model._meta.get_field("field"),
id="fields.W224",
),
]
self.assertEqual(
Model._meta.get_field("field").check(databases={"default"}),
expected_warnings,
)