This is a fun one :-)
Working on an EAV, we inject a generic relationship handler at runtime in a model.
model_cls
is any class, and a EavValue
class have a generic relation pointing to it. It works fine from EavValues
to a model_cls
, but on the other way we need to inject an accessor to ease things:
generic_relation = generic.GenericRelation(EavValue,
object_id_field='entity_id',
content_type_field='entity_ct',
related_name=model_cls.__name__)
generic_relation.contribute_to_class(model_cls, 'eav_values')
Again, we do that at runtime because we want to make it work with untouchable 3rd party libs.
While unittesting with a Patient
class as model_cls
, we get the following error:
eav_ng.patient: Accessor for m2m field 'eav_values' clashes with related m2m field 'EavValue.Patient'. Add a related_name argument to the definition for 'eav_values'.
Now, we thought the easy fix was to change either the second parameter of contribute_to_class
or related_name
in GenericRelation
, but it doesn't! We get exactly the same error, only with different name.
Second strange thing, running the same unittests with Sqlite instead of MySql: all pass.
What's more, no matters the order or the tests, we always get this error at the second tests. Since this process happen in a register
method and that we call register
and unregister
at setup and tear down, I'm guessing our unregister
method is imperfect.
Last strange fact: we get the error while running unittest, but we are unable to reproduce it manually. Worst, on my colleague computer, it doesn't get the error while we are using the same version of Python, Django, Ubuntu and MySQL.
We solved a lot of hard ones but we are kind of stuck on this one so any clue appreciated.
UPDATE:
New clues for this great game:
Errors are raised from this snippet in django.core.management.validation, on line 245 (django 1.2.1):
for r in rel_opts.get_all_related_many_to_many_objects():
if r.field is not f:
if r.get_accessor_name() == rel_name:
e.add(opts, "Accessor for m2m field '%s' clashes with related m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
if r.get_accessor_name() == rel_query_name:
e.add(opts, "Reverse query name for m2m field '%s' clashes with related m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
For us r.get_accessor_name() == rel_name
is True, as both are "Patient".
UPDATE 2:
When we add an app that register
a model. Any model, the problem doesn't appear anymore. So much for the unregister
theory...
We at two symmetrical errors (both sides of the relation). Removing related_name
suppress one of the errors 0_o