Opened 13 years ago

Closed 13 years ago

Last modified 12 years ago

#14948 closed (fixed)

Broken routers in 1.2.4: type object 'ModelBase' has no attribute '_meta'

Reported by: shell_dweller Owned by: nobody
Component: Database layer (models, ORM) Version: 1.2
Severity: Keywords: router, ManyToManyField, blocker, regression
Cc: s.kuzmenko@…, django@…, fwenzel@…, james@…, conrad666@…, typeshige@…, brian@…, django@…, hgeerts@… Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

There is a problem with using routers in the latest security release 1.2.4. The details are somewhat murky for a lay person but here's the gist of it:

  • Attempt to save any model with a ManyToManyField fails in router when trying to execute db_for_write:
    def db_for_write(self, model, **hints):
        if model._meta.app_label == 'my_app_label':
            return 'my_db_name'
        return None

Error message is "type object 'ModelBase' has no attribute '_meta'".

It seems that the error occurs in django/db/models/fields/related.py, line 624 where self.through.__class__ returns ModelBase whereas prior to 1.2.4 it used to be a user defined class.

Here's the stack trace:

  [ snip ]

  File "/usr/lib/python2.4/site-packages/django/forms/models.py", line 375, in save
    fail_message, commit, construct=False)

  File "/usr/lib/python2.4/site-packages/django/forms/models.py", line 87, in save_instance
    save_m2m()

  File "/usr/lib/python2.4/site-packages/django/forms/models.py", line 83, in save_m2m
    f.save_form_data(instance, cleaned_data[f.name])

  File "/usr/lib/python2.4/site-packages/django/db/models/fields/related.py", line 1144, in save_form_data
    setattr(instance, self.attname, data)

  File "/usr/lib/python2.4/site-packages/django/db/models/fields/related.py", line 730, in __set__
    manager.clear()

  File "/usr/lib/python2.4/site-packages/django/db/models/fields/related.py", line 506, in clear
    self._clear_items(self.source_field_name)

  File "/usr/lib/python2.4/site-packages/django/db/models/fields/related.py", line 624, in _clear_items
    db = router.db_for_write(self.through.__class__, instance=self.instance)

  File "/usr/lib/python2.4/site-packages/django/db/utils.py", line 134, in _route_db
    chosen_db = method(model, **hints)

  File "/path/to/my/app/routers.py", line 10, in db_for_write
    if model._meta.app_label == 'my_label':

AttributeError: type object 'ModelBase' has no attribute '_meta'

Attachments (3)

django-1.2-router_related_accessor.patch (4.8 KB ) - added by Harm Geerts <hgeerts@…> 13 years ago.
patch with testcase for django-1.2, trunk will need to be patched as well
14948-1.2.X-r15186.diff (1.2 KB ) - added by Ramiro Morales 13 years ago.
Patch by Harm Geerts updated to 1.2.x banch status as of now
django-1.2-router_related_accessor-2.patch (3.8 KB ) - added by Harm Geerts <hgeerts@…> 13 years ago.
new patch based on changes from #13668 with tests to cover changes in django.db.models.base

Download all attachments as: .zip

Change History (24)

comment:1 by farialimaa, 13 years ago

Cc: django@… added

Same problem here - an easy way to reproduce:

bash-3.2$ ./manage.py shell
Python 2.5.4 (r254:67917, Dec 23 2008, 14:57:27)
[GCC 4.0.1 (Apple Computer, Inc. build 5363)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.contrib.auth.models import User
>>> user = User()
>>> user.username = "testuser"
>>> user.save()
>>> user.delete()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/path/to//django/db/models/base.py", line 660, in delete
    self._collect_sub_objects(seen_objs)
  File "/path/to//django/db/models/base.py", line 625, in _collect_sub_objects
    db = router.db_for_write(f.rel.through.__class__, instance=self)
  File "/path/to//django/db/utils.py", line 134, in _route_db
    chosen_db = method(model, **hints)
  File "/path/to/my/app/routers.py", line 35, in db_for_write
    object_name = model._meta.object_name
AttributeError: type object 'ModelBase' has no attribute '_meta'
>>>  

comment:2 by farialimaa, 13 years ago

(assuming you have created a registered a router that uses "model._meta.object_name", of course)

comment:3 by fwenzel, 13 years ago

Cc: fwenzel@… added

I can confirm this. A simple workaround would probably be to catch this in the router, but this would be a duct-tape fix.

comment:4 by James Socol, 13 years ago

Cc: james@… added

comment:5 by anonymous, 13 years ago

Cc: conrad666@… added

comment:6 by typeshige, 13 years ago

Cc: typeshige@… added

comment:7 by Russell Keith-Magee, 13 years ago

milestone: 1.3
Triage Stage: UnreviewedAccepted

Ok - this is a pretty major regression.

comment:8 by brian@…, 13 years ago

Cc: brian@… added

comment:9 by Russell Keith-Magee, 13 years ago

Keywords: blocker regression added

comment:10 by brendoncrawford, 13 years ago

+1

comment:11 by brendoncrawford, 13 years ago

Cc: django@… added

comment:12 by intrepidweb, 13 years ago

This bug broke my site when I upgraded to 1.2.4, forcing me to roll back to 1.2.1 (after being deluged hundreds of errors in a few minutes). I am surprised more people haven't spoken up. Thanks for the hard work.

in reply to:  12 comment:13 by brendoncrawford, 13 years ago

Replying to intrepidweb:

This bug broke my site when I upgraded to 1.2.4, forcing me to roll back to 1.2.1 (after being deluged hundreds of errors in a few minutes). I am surprised more people haven't spoken up. Thanks for the hard work.

You should be able to at least upgrade to 1.2.3 without encountering this problem.

comment:14 by Harm Geerts <hgeerts@…>, 13 years ago

The router check on rel.through may have been broken for some time but was hidden because the ConnectionRouter catched it's AttributeError
We can thank the following ticket for showing us this bug: http://code.djangoproject.com/ticket/14870
The patch cannot be reverted since the router call is broken and needs to be fixed.

On my project the error was raised in a different code path.

  File "/home/harm/django12/django/core/management/commands/loaddata.py", line 174, in handle
    obj.save(using=using)
  File "/home/harm/django12/django/core/serializers/base.py", line 168, in save
    setattr(self.object, accessor_name, object_list)
  File "/home/harm/django12/django/db/models/fields/related.py", line 730, in __set__
    manager.clear()
  File "/home/harm/django12/django/db/models/fields/related.py", line 506, in clear
    self._clear_items(self.source_field_name)
  File "/home/harm/django12/django/db/models/fields/related.py", line 624, in _clear_items
    db = router.db_for_write(self.through.__class__, instance=self.instance)
  File "/home/harm/django12/django/db/utils.py", line 134, in _route_db
    chosen_db = method(model, **hints)
  File "/home/harm/lascon/lascon/router.py", line 18, in db_for_write
    if model._meta.app_label == 'exact' and model._meta.object_name != 'ExactProfile':
AttributeError: type object 'ModelBase' has no attribute '_meta'

comment:15 by Harm Geerts <hgeerts@…>, 13 years ago

Cc: hgeerts@… added

by Harm Geerts <hgeerts@…>, 13 years ago

patch with testcase for django-1.2, trunk will need to be patched as well

comment:16 by Harm Geerts <hgeerts@…>, 13 years ago

Has patch: set

comment:17 by Ramiro Morales, 13 years ago

See also #13668 that reported a similar problem for M2M relationships that *don't* specify a through model. It was fixed in r15185 (and r15186 for the 1.2.X branch). The tests modification might clash with the ones included in this patch and can be unified (FaultyRouter and AttributeErrorRouter).

comment:18 by Ramiro Morales, 13 years ago

Patch needs improvement: set

Actually the fix for #13668 is a subset of the fixes proposed here. What would be needed to close this ticket is a test case for the django/db/models/base.py modifications, currently the tests proposed in the patch attached to this ticket, passes irrespective of if the changes to that file are present.

by Ramiro Morales, 13 years ago

Attachment: 14948-1.2.X-r15186.diff added

Patch by Harm Geerts updated to 1.2.x banch status as of now

by Harm Geerts <hgeerts@…>, 13 years ago

new patch based on changes from #13668 with tests to cover changes in django.db.models.base

comment:19 by Ramiro Morales, 13 years ago

Resolution: fixed
Status: newclosed

(In [15207]) [1.2.X] Fixed #14948 -- Fixed a couple of cases where invalid model classes were passed to the database router when collecting reverse foreign key and many to many relationships. Thanks shell_dweller for the report and Harm Geerts for the patch.

This also enhances tests added in r15186.

Code in SVN trunk doesn't suffer from this problem because it was refactored in r14507.

comment:20 by Ramiro Morales, 13 years ago

(In [15208]) Enhanced slightly the tests added in r15185 to demonstrate that #14948 doesn't affect trunk. Refs #14948. Thanks Harm Geerts.

comment:21 by Jacob, 12 years ago

milestone: 1.3

Milestone 1.3 deleted

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