Forbid field shadowing in multi-table inheritance.
(cls)
| 1999 | |
| 2000 | @classmethod |
| 2001 | def _check_field_name_clashes(cls): |
| 2002 | """Forbid field shadowing in multi-table inheritance.""" |
| 2003 | errors = [] |
| 2004 | used_fields = {} # name or attname -> field |
| 2005 | |
| 2006 | # Check that multi-inheritance doesn't cause field name shadowing. |
| 2007 | for parent in cls._meta.all_parents: |
| 2008 | for f in parent._meta.local_fields: |
| 2009 | clash = used_fields.get(f.name) or used_fields.get(f.attname) or None |
| 2010 | if clash: |
| 2011 | errors.append( |
| 2012 | checks.Error( |
| 2013 | "The field '%s' from parent model " |
| 2014 | "'%s' clashes with the field '%s' " |
| 2015 | "from parent model '%s'." |
| 2016 | % (clash.name, clash.model._meta, f.name, f.model._meta), |
| 2017 | obj=cls, |
| 2018 | id="models.E005", |
| 2019 | ) |
| 2020 | ) |
| 2021 | used_fields[f.name] = f |
| 2022 | used_fields[f.attname] = f |
| 2023 | |
| 2024 | # Check that fields defined in the model don't clash with fields from |
| 2025 | # parents, including auto-generated fields like multi-table inheritance |
| 2026 | # child accessors. |
| 2027 | for parent in cls._meta.all_parents: |
| 2028 | for f in parent._meta.get_fields(): |
| 2029 | if f not in used_fields: |
| 2030 | used_fields[f.name] = f |
| 2031 | |
| 2032 | # Check that parent links in diamond-shaped MTI models don't clash. |
| 2033 | for parent_link in cls._meta.parents.values(): |
| 2034 | if not parent_link: |
| 2035 | continue |
| 2036 | clash = used_fields.get(parent_link.name) or None |
| 2037 | if clash: |
| 2038 | errors.append( |
| 2039 | checks.Error( |
| 2040 | f"The field '{parent_link.name}' clashes with the field " |
| 2041 | f"'{clash.name}' from model '{clash.model._meta}'.", |
| 2042 | obj=cls, |
| 2043 | id="models.E006", |
| 2044 | ) |
| 2045 | ) |
| 2046 | |
| 2047 | for f in cls._meta.local_fields: |
| 2048 | clash = used_fields.get(f.name) or used_fields.get(f.attname) or None |
| 2049 | # Note that we may detect clash between user-defined non-unique |
| 2050 | # field "id" and automatically added unique field "id", both |
| 2051 | # defined at the same model. This special case is considered in |
| 2052 | # _check_id_field and here we ignore it. |
| 2053 | id_conflict = ( |
| 2054 | f.name == "id" and clash and clash.name == "id" and clash.model == cls |
| 2055 | ) |
| 2056 | if clash and not id_conflict: |
| 2057 | errors.append( |
| 2058 | checks.Error( |
no test coverage detected