﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
37365	models.E007 misses duplicate columns when one db_column is explicitly quoted	Matthew Schinckel		"The models.E007 system check can miss duplicate database columns when one db_column is explicitly quoted and another is not. The strings differ, but the configured backend can render them as the same SQL identifier.

This issue appears to be mostly the same across different backends.

== Reproduction ==

This test uses the configured backend's own identifier quoting. It does not query the database.

{{{#!python
from django.db import connection, models
from django.test import SimpleTestCase
from django.test.utils import isolate_apps


class QuotedColumnTests(SimpleTestCase):
    @isolate_apps()
    def test_quoted_column_collision(self) -> None:
        quoted_name = connection.ops.quote_name(""id"")

        class Example(models.Model):
            key = models.IntegerField(primary_key=True, db_column=quoted_name)
            score = models.IntegerField(db_column=""id"")

            class Meta:
                app_label = ""example""

        self.assertIn(""models.E007"", {error.id for error in Example.check()})
}}}

Actual result: Example.check() returns [], so the assertion fails.

Expected result: models.E007, because both fields map to the same physical column. Using the unquoted string ""id"" for both fields correctly produces that error.

== Backend examples ==

The equivalent spellings depend on the backend:

* SQLite: {{{id}}} and {{{""id""}}} both render as {{{""id""}}}. [https://github.com/django/django/blob/6.1.1/django/db/backends/sqlite3/operations.py Source]
* MySQL: {{{id}}} and {{{`id`}}} both render as {{{`id`}}}. The quoted spelling must use backticks; double quotes would represent a different identifier. [https://github.com/django/django/blob/6.1.1/django/db/backends/mysql/operations.py Source]
* Oracle: {{{id}}} and {{{""id""}}} both render as {{{""ID""}}}, because Django's Oracle quote_name() uppercases its result. [https://github.com/django/django/blob/6.1.1/django/db/backends/oracle/operations.py Source]
* PostgreSQL: {{{id}}} and {{{""id""}}} both render as {{{""id""}}}. [https://github.com/django/django/blob/6.1.1/django/db/backends/postgresql/operations.py Source]

== Cause ==

[https://github.com/django/django/blob/6.1.1/django/db/models/base.py Model._check_column_name_clashes()] compares the original field.column strings without accounting for backend identifier handling. It accepts declarations that would generate duplicate column names.

The desired behaviour is to recognise equivalent identifiers and report the collision, rather than permit duplicate columns. Any fix should account for backend-specific rules rather than simply stripping double quotes.

== Versions checked ==

Runtime reproduction: Django 5.2.17, Python 3.11.6, with SQLite and PostgreSQL backend configurations. The checks do not query a database.

6.1.1 appears to have the same issue based on inspection of its released source. The shared check and the four backends' quote_name() implementations are unchanged from 5.2.17. MySQL and Oracle findings are source verification only; no integration tests were run against those databases.
"	Bug	new	Core (System checks)	6.1	Normal		db_column quoting system-checks		Unreviewed	0	0	0	0	0	0
