127 lines
6.8 KiB
Diff
127 lines
6.8 KiB
Diff
Based on my knowledge of Django's inspectdb command, I can create the patch. The inspectdb command is located in `django/core/management/commands/inspectdb.py`. The issue is that when multiple ForeignKey fields in a model point to the same related model, Django raises E304 error about clashing reverse accessors.
|
|
|
|
The fix needs to:
|
|
1. Track which related models have been used for foreign key relationships in each table
|
|
2. When a related model is used more than once, add `related_name` to all ForeignKey fields pointing to that model to avoid the reverse accessor clash
|
|
|
|
Here is the patch for Django's inspectdb command:
|
|
|
|
--- a/django/core/management/commands/inspectdb.py
|
|
+++ b/django/core/management/commands/inspectdb.py
|
|
@@ -123,6 +123,15 @@ class Command(BaseCommand):
|
|
if table_name in relations:
|
|
raise CommandError("Multiple models have the same name: %s" % att_name)
|
|
|
|
+ # Track how many ForeignKey/OneToOneField relations point to each
|
|
+ # related table to detect when we need related_name.
|
|
+ used_relations = {}
|
|
+ for column_name in (
|
|
+ x for x in relations if x[0] not in known_models
|
|
+ ):
|
|
+ ref_db_table = relations[column_name][1]
|
|
+ used_relations[ref_db_table] = used_relations.get(ref_db_table, 0) + 1
|
|
+
|
|
for column_name in connection.introspection.get_table_description(
|
|
cursor, table_name
|
|
):
|
|
@@ -149,6 +158,9 @@ class Command(BaseCommand):
|
|
ref_db_table = relations[column_name[0]][1]
|
|
if ref_db_table == table_name:
|
|
extra_params["to"] = "'self'"
|
|
+ # Add related_name if multiple FKs point to the same table.
|
|
+ if used_relations.get(ref_db_table, 0) > 1:
|
|
+ extra_params["related_name"] = "'%s'" % (att_name + "_set")
|
|
else:
|
|
extra_params["to"] = (
|
|
"'%s'" % relations[column_name[0]][1]
|
|
|
|
Wait, let me reconsider the Django inspectdb structure more carefully. Based on my knowledge, the code structure is different. Let me provide a more accurate patch:
|
|
|
|
--- a/django/core/management/commands/inspectdb.py
|
|
+++ b/django/core/management/commands/inspectdb.py
|
|
@@ -62,6 +62,16 @@ class Command(BaseCommand):
|
|
yield "# * Make sure each ForeignKey and OneToOneField has `on_delete` set to the desired behavior"
|
|
yield (
|
|
"# * Remove `managed = False` lines if you wish to allow "
|
|
"Django to create, modify, and delete the table"
|
|
)
|
|
yield (
|
|
"# Feel free to rename the models, but don't rename db_table values or "
|
|
"field names."
|
|
)
|
|
yield "from %s import models" % self.db_module
|
|
known_models = []
|
|
tables_to_introspect = options["table"] or connection.introspection.table_names(cursor)
|
|
|
|
for table_name in tables_to_introspect:
|
|
if table_name_filter is not None and callable(table_name_filter):
|
|
if not table_name_filter(table_name):
|
|
continue
|
|
try:
|
|
try:
|
|
relations = connection.introspection.get_relations(
|
|
cursor, table_name
|
|
)
|
|
except NotImplementedError:
|
|
relations = {}
|
|
try:
|
|
constraints = connection.introspection.get_constraints(
|
|
cursor, table_name
|
|
)
|
|
except NotImplementedError:
|
|
constraints = {}
|
|
primary_key_columns = (
|
|
connection.introspection.get_primary_key_columns(
|
|
cursor, table_name
|
|
)
|
|
)
|
|
primary_key_column = (
|
|
primary_key_columns[0] if primary_key_columns else None
|
|
)
|
|
unique_columns = [
|
|
c["columns"][0]
|
|
for c in constraints.values()
|
|
if c["unique"] and len(c["columns"]) == 1
|
|
]
|
|
+ # Count how many ForeignKeys point to each related table
|
|
+ # to determine if we need to add related_name.
|
|
+ used_relations = {}
|
|
+ for rel_column in relations:
|
|
+ ref_table = relations[rel_column][1]
|
|
+ used_relations[ref_table] = used_relations.get(ref_table, 0) + 1
|
|
table_description = connection.introspection.get_table_description(
|
|
cursor, table_name
|
|
)
|
|
|
|
I realize I need to be more precise about the exact location in the file. Based on the Django codebase structure, let me provide the correct patch:
|
|
|
|
--- a/django/core/management/commands/inspectdb.py
|
|
+++ b/django/core/management/commands/inspectdb.py
|
|
@@ -63,6 +63,11 @@ class Command(BaseCommand):
|
|
constraints = {}
|
|
primary_key_column = connection.introspection.get_primary_key_column(cursor, table_name)
|
|
unique_columns = [
|
|
c['columns'][0]
|
|
for c in constraints.values()
|
|
if c['unique'] and len(c['columns']) == 1
|
|
]
|
|
+ # Count how many times each related table is referenced
|
|
+ # to determine if related_name is needed to avoid clashes.
|
|
+ used_relations = {}
|
|
+ for column_name in relations:
|
|
+ ref_db_table = relations[column_name][1]
|
|
+ used_relations[ref_db_table] = used_relations.get(ref_db_table, 0) + 1
|
|
table_description = connection.introspection.get_table_description(cursor, table_name)
|
|
except Exception as e:
|
|
yield "# Unable to inspect table '%s'" % table_name
|
|
@@ -127,6 +132,10 @@ class Command(BaseCommand):
|
|
extra_params['to'] = "'self'"
|
|
else:
|
|
extra_params['to'] = table2model(ref_db_table)
|
|
+ # If multiple ForeignKeys point to the same table,
|
|
+ # add related_name to avoid reverse accessor clashes.
|
|
+ if used_relations.get(ref_db_table, 0) > 1:
|
|
+ extra_params['related_name'] = "'%s'" % (att_name + '_set')
|
|
else:
|
|
extra_params['to'] = table2model(ref_db_table)
|
|
if ref_db_column != 'id':
|