From 393e4822ae3abf19115f4022d208df106b7cff85 Mon Sep 17 00:00:00 2001
From: Clay McClure <clay@daemons.net>
Date: Wed, 14 Mar 2012 15:41:48 -0400
Subject: [PATCH] Fixed model validation when using hidden (+) related_names
on M2M fields
Fixes #15932.
There is a comment in that same ticket about the ORM not working
properly when hidden (+) related_names are used, but that could
be tracked and fixed as a separate ticket. This commit fixes
the validation issue originally reported.
---
django/core/management/validation.py | 6 +-----
.../invalid_models/invalid_models/models.py | 13 +++++++++++++
2 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/django/core/management/validation.py b/django/core/management/validation.py
index 4833337..3038382 100644
|
a
|
b
|
def get_validation_errors(outfile, app=None):
|
| 244 | 244 | rel_opts = f.rel.to._meta |
| 245 | 245 | rel_name = RelatedObject(f.rel.to, cls, f).get_accessor_name() |
| 246 | 246 | rel_query_name = f.related_query_name() |
| 247 | | # If rel_name is none, there is no reverse accessor (this only |
| 248 | | # occurs for symmetrical m2m relations to self). If this is the |
| 249 | | # case, there are no clashes to check for this field, as there are |
| 250 | | # no reverse descriptors for this field. |
| 251 | | if rel_name is not None: |
| | 247 | if not f.rel.is_hidden(): |
| 252 | 248 | for r in rel_opts.fields: |
| 253 | 249 | if r.name == rel_name: |
| 254 | 250 | e.add(opts, "Accessor for m2m field '%s' clashes with field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name)) |
diff --git a/tests/modeltests/invalid_models/invalid_models/models.py b/tests/modeltests/invalid_models/invalid_models/models.py
index ed69fb6..b74f1da 100644
|
a
|
b
|
class Target(models.Model):
|
| 30 | 30 | clash2 = models.CharField(max_length=10) |
| 31 | 31 | |
| 32 | 32 | clash1_set = models.CharField(max_length=10) |
| | 33 | hiddenrelation_set = models.CharField(max_length=10) |
| 33 | 34 | |
| 34 | 35 | class Clash1(models.Model): |
| 35 | 36 | src_safe = models.CharField(max_length=10) |
| … |
… |
class Clash2(models.Model):
|
| 46 | 47 | m2m_1 = models.ManyToManyField(Target, related_name='id') |
| 47 | 48 | m2m_2 = models.ManyToManyField(Target, related_name='src_safe') |
| 48 | 49 | |
| | 50 | class HiddenRelation(models.Model): |
| | 51 | "Hidden relations (related_name[-1] == '+') should not clash" |
| | 52 | foreign = models.ForeignKey(Target, related_name='+') |
| | 53 | m2m = models.ManyToManyField(Target, related_name='+') |
| | 54 | |
| | 55 | class MultipleHiddenRelation(models.Model): |
| | 56 | "Multiple hidden relations (related_name[-1] == '+') should not clash with each other" |
| | 57 | foreign1 = models.ForeignKey(Target, related_name='+') |
| | 58 | foreign2 = models.ForeignKey(Target, related_name='+') |
| | 59 | m2m1 = models.ManyToManyField(Target, related_name='+') |
| | 60 | m2m2 = models.ManyToManyField(Target, related_name='+') |
| | 61 | |
| 49 | 62 | class Target2(models.Model): |
| 50 | 63 | clash3 = models.CharField(max_length=10) |
| 51 | 64 | foreign_tgt = models.ForeignKey(Target) |