Code

Opened 4 years ago

Closed 3 years ago

Last modified 3 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: UI/UX:

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@…> 4 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 4 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@…> 4 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 Changed 4 years ago by farialimaa

  • Cc django@… added
  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

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 Changed 4 years ago by farialimaa

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

comment:3 Changed 4 years ago by fwenzel

  • 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 Changed 4 years ago by jsocol

  • Cc james@… added

comment:5 Changed 4 years ago by anonymous

  • Cc conrad666@… added

comment:6 Changed 4 years ago by typeshige

  • Cc typeshige@… added

comment:7 Changed 4 years ago by russellm

  • milestone set to 1.3
  • Triage Stage changed from Unreviewed to Accepted

Ok - this is a pretty major regression.

comment:8 Changed 4 years ago by brian@…

  • Cc brian@… added

comment:9 Changed 4 years ago by russellm

  • Keywords ManyToManyField, blocker, regression added; ManyToManyField removed

comment:10 Changed 4 years ago by brendoncrawford

+1

comment:11 Changed 4 years ago by brendoncrawford

  • Cc django@… added

comment:12 follow-up: Changed 4 years ago by 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.

comment:13 in reply to: ↑ 12 Changed 4 years ago by brendoncrawford

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 Changed 4 years ago by Harm Geerts <hgeerts@…>

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 Changed 4 years ago by Harm Geerts <hgeerts@…>

  • Cc hgeerts@… added

Changed 4 years ago by Harm Geerts <hgeerts@…>

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

comment:16 Changed 4 years ago by Harm Geerts <hgeerts@…>

  • Has patch set

comment:17 Changed 4 years ago by ramiro

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 Changed 4 years ago by ramiro

  • 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.

Changed 4 years ago by ramiro

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

Changed 4 years ago by Harm Geerts <hgeerts@…>

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

comment:19 Changed 3 years ago by ramiro

  • Resolution set to fixed
  • Status changed from new to closed

(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 Changed 3 years ago by ramiro

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

comment:21 Changed 3 years ago by jacob

  • milestone 1.3 deleted

Milestone 1.3 deleted

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.