Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions django/db/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2220,6 +2220,20 @@ def _check_local_fields(cls, fields, option):
id="models.E048",
)
)
elif (
isinstance(field.remote_field, ForeignObjectRel)
and field not in cls._meta.local_concrete_fields
and len(field.from_fields) > 1
):
errors.append(
checks.Error(
f"{option!r} refers to a ForeignObject {field_name!r} with "
"multiple 'from_fields', which is not supported for that "
"option.",
obj=cls,
id="models.E049",
)
)
elif field not in cls._meta.local_fields:
errors.append(
checks.Error(
Expand Down
7 changes: 6 additions & 1 deletion django/db/models/indexes.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,12 @@ def check(self, model, connection):
)
errors.extend(
model._check_local_fields(
{*self.fields, *self.include, *references}, "indexes"
{
*[field for field, _ in self.fields_orders],
*self.include,
*references,
},
"indexes",
)
)
# Database-feature checks:
Expand Down
3 changes: 3 additions & 0 deletions docs/ref/checks.txt
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,9 @@ Models
* **models.E048**: ``constraints/indexes/unique_together`` refers to a
``CompositePrimaryKey`` ``<field name>``, but ``CompositePrimaryKey``\s are
not supported for that option.
* **models.E049**: ``constraints/indexes/unique_together`` refers to a
``ForeignObject`` ``<field name>`` with multiple ``from_fields``, which is
not supported for that option.

Management Commands
-------------------
Expand Down
4 changes: 4 additions & 0 deletions docs/releases/6.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,10 @@ Models
don't support it (MySQL and MariaDB), the fields are marked as deferred to
trigger a refresh on subsequent accesses.

* Using a :ref:`ForeignObject <cpk-and-relations>` with multiple
``from_fields`` in Model indexes, constraints, or :attr:`unique_together
<django.db.models.Options.unique_together>` now emits a system check error.

Pagination
~~~~~~~~~~

Expand Down
57 changes: 57 additions & 0 deletions tests/invalid_models_tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,54 @@ class Meta:
],
)

def test_pointing_to_foreign_object(self):
class Reference(models.Model):
reference_id = models.IntegerField(unique=True)

class ReferenceTab(models.Model):
reference_id = models.IntegerField()
reference = models.ForeignObject(
Reference,
from_fields=["reference_id"],
to_fields=["reference_id"],
on_delete=models.CASCADE,
)

class Meta:
unique_together = [["reference"]]

self.assertEqual(ReferenceTab.check(), [])

def test_pointing_to_foreign_object_multi_column(self):
class Reference(models.Model):
reference_id = models.IntegerField(unique=True)
code = models.CharField(max_length=1)

class ReferenceTabMultiple(models.Model):
reference_id = models.IntegerField()
code = models.CharField(max_length=1)
reference = models.ForeignObject(
Reference,
from_fields=["reference_id", "code"],
to_fields=["reference_id", "code"],
on_delete=models.CASCADE,
)

class Meta:
unique_together = [["reference"]]

self.assertEqual(
ReferenceTabMultiple.check(),
[
Error(
"'unique_together' refers to a ForeignObject 'reference' with "
"multiple 'from_fields', which is not supported for that option.",
obj=ReferenceTabMultiple,
id="models.E049",
),
],
)


@isolate_apps("invalid_models_tests")
class IndexesTests(TestCase):
Expand All @@ -185,6 +233,15 @@ class Meta:
],
)

def test_pointing_to_desc_field(self):
class Model(models.Model):
name = models.CharField(max_length=100)

class Meta:
indexes = [models.Index(fields=["-name"], name="index_name")]

self.assertEqual(Model.check(databases=self.databases), [])

def test_pointing_to_m2m_field(self):
class Model(models.Model):
m2m = models.ManyToManyField("self")
Expand Down