Opened 10 years ago

Closed 10 years ago

#22850 closed Uncategorized (invalid)

False reverse accessor clash with inherited classes

Reported by: Ilya Semenov Owned by: nobody
Component: Database layer (models, ORM) Version: 1.4
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Consider the following models.py:

class Base(models.Model):
        field1 = models.IntegerField()

class Derived(Base):
        field2 = models.IntegerField()
        
class Pointer(models.Model):
        base = models.ForeignKey(Base)
        derived = models.ForeignKey(Derived)

it crashes:

Unhandled exception in thread started by <function wrapper at 0x105daab90>
Traceback (most recent call last):
  File "/Users/semenov/work/project/venv/src/django/django/utils/autoreload.py", line 170, in wrapper
    fn(*args, **kwargs)
  File "/Users/semenov/work/project/venv/src/django/django/core/management/commands/runserver.py", line 104, in inner_run
    self.validate(display_num_errors=True)
  File "/Users/semenov/work/project/venv/src/django/django/core/management/base.py", line 361, in validate
    return self.check(app_configs=app_configs, display_num_errors=display_num_errors)
  File "/Users/semenov/work/project/venv/src/django/django/core/management/base.py", line 413, in check
    raise CommandError(msg)
django.core.management.base.CommandError: System check identified some issues:

ERRORS:
project.Pointer.derived: (fields.E304) Reverse accessor for 'Pointer.derived' clashes with reverse accessor for 'Pointer.base'.
	HINT: Add or change a related_name argument to the definition for 'Pointer.derived' or 'Pointer.base'.

However, if I rewrite it to the semantically equivalent form:

class AbstractBase(models.Model):
        field1 = models.IntegerField()
        class Meta:
                abstract = True

class Base(AbstractBase):
        pass

class Derived(AbstractBase):
        field2 = models.IntegerField()
        
class Pointer(models.Model):
        base = models.ForeignKey(Base)
        derived = models.ForeignKey(Derived)

it works fine.

EXPECTED RESULT: both cases should work identically (create identical database tables and ORM accessors).

Change History (3)

comment:1 by Tim Graham, 10 years ago

Did the first version work in older versions of Django?

comment:2 by Ilya Semenov, 10 years ago

Version: 1.7-beta-21.4

I tested both cases on Django 1.4.X branch, the first version crashed with similar error:

Unhandled exception in thread started by <bound method Command.inner_run of <django.core.management.commands.runserver.Command object at 0x101e3b110>>
Traceback (most recent call last):
  File "/Users/semenov/work/project/venv/lib/python2.7/site-packages/django/core/management/commands/runserver.py", line 91, in inner_run
    self.validate(display_num_errors=True)
  File "/Users/semenov/work/project/venv/lib/python2.7/site-packages/django/core/management/base.py", line 270, in validate
    raise CommandError("One or more models did not validate:\n%s" % error_text)
django.core.management.base.CommandError: One or more models did not validate:
project.pointer: Accessor for field 'derived' clashes with related field 'Derived.pointer_set'. Add a related_name argument to the definition for 'derived'.

The second version worked fine.

comment:3 by Tim Graham, 10 years ago

Resolution: invalid
Status: newclosed

The two forms are really not equivalent -- they will created different database tables. An explanation of the differences is here.

Having a ForeignKey to both Base and Derived in the first example in really redundant as they will refer to the same database column (Base.id). I believe that's the reason for the validation error.

Note: See TracTickets for help on using tickets.
Back to Top