Opened 16 years ago

Closed 13 years ago

Last modified 10 years ago

#10405 closed Bug (fixed)

quoted class names in foreign key definition causes 'str' object has no attribute '_default_manager'

Reported by: danbrwn Owned by: Armin Ronacher
Component: Database layer (models, ORM) Version:
Severity: Normal Keywords: foreign, key, quoted, dceu2011
Cc: Ramiro Morales, jamespic@…, subsume@…, tom@…, eduardocereto@…, djsnickles@…, michaelvantellingen@…, seocam@… Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description (last modified by Alex Gaynor)

Myself and another discovered the following error with the code shown. In both cases we had quoted strings for reference class. In both cases importing the specific class and then referencing without the quotes solved the problem. I tried through IRC channel to get a resolution. Downloaded code, wiped out site-packages/django dir before running setup, took other suggestions. Nothing worked except removing the quotes as suggested on the django-users group. I am running Debian Etch final. Python 2.5 and Django 1.0.2, Apache2 with mod_python
AttributeError: 'str' object has no attribute '_default_manager


MOD_PYTHON ERROR

ProcessId:      2637
Interpreter:    'TS1.unassigned-domain'

ServerName:     'TS1.unassigned-domain'
DocumentRoot:   '/var/www/'

URI:            '/sipprovision/admin'
Location:       '/sipprovision/'
Directory:      None
Filename:       '/var/www/sipprovision/admin'
PathInfo:       ''

Phase:          'PythonHandler'
Handler:        'django.core.handlers.modpython'

Traceback (most recent call last):

  File "/usr/lib/python2.5/site-packages/mod_python/importer.py", line 1537, in HandlerDispatch
    default=default_handler, arg=req, silent=hlist.silent)

  File "/usr/lib/python2.5/site-packages/mod_python/importer.py", line 1229, in _process_target
    result = _execute_target(config, req, object, arg)

  File "/usr/lib/python2.5/site-packages/mod_python/importer.py", line 1128, in _execute_target
    result = object(arg)

  File "/usr/lib/python2.5/site-packages/django/core/handlers/modpython.py", line 228, in handler
    return ModPythonHandler()(req)

  File "/usr/lib/python2.5/site-packages/django/core/handlers/modpython.py", line 201, in __call__
    response = self.get_response(request)

  File "/usr/lib/python2.5/site-packages/django/core/handlers/base.py", line 67, in get_response
    response = middleware_method(request)

  File "/usr/lib/python2.5/site-packages/django/middleware/common.py", line 56, in process_request
    if (not _is_valid_path(request.path_info) and

  File "/usr/lib/python2.5/site-packages/django/middleware/common.py", line 142, in _is_valid_path
    urlresolvers.resolve(path)

  File "/usr/lib/python2.5/site-packages/django/core/urlresolvers.py", line 246, in resolve
    return get_resolver(urlconf).resolve(path)

  File "/usr/lib/python2.5/site-packages/django/core/urlresolvers.py", line 179, in resolve
    for pattern in self.urlconf_module.urlpatterns:

  File "/usr/lib/python2.5/site-packages/django/core/urlresolvers.py", line 198, in _get_urlconf_module
    self._urlconf_module = __import__(self.urlconf_name, {}, {}, [''])

  File "/var/www/sipprovision/urls.py", line 2, in <module>
    from extensions.models import Extension

  File "/var/www/sipprovision/extensions/models.py", line 42, in <module>
    class ExtensionForm(ModelForm):

  File "/usr/lib/python2.5/site-packages/django/forms/models.py", line 195, in __new__
    opts.exclude, formfield_callback)

  File "/usr/lib/python2.5/site-packages/django/forms/models.py", line 162, in fields_for_model
    formfield = formfield_callback(f)

  File "/usr/lib/python2.5/site-packages/django/forms/models.py", line 177, in <lambda>
    lambda f: f.formfield())

  File "/usr/lib/python2.5/site-packages/django/db/models/fields/related.py", line 694, in formfield
    'queryset': self.rel.to._default_manager.complex_filter(

AttributeError: 'str' object has no attribute '_default_manager'


------------------------- with this model -------------------------------------
from django.db import models
from sipconfig import *
from django.forms import ModelForm, fields, TextInput, IntegerField
# Create your models here.
class Plc(models.Model):
    name=models.CharField(max_length=30)
    ip_addr=models.IPAddressField()
    ip_port=models.IntegerField(default=9600)
    plc_net=models.IntegerField()
    plc_node=models.IntegerField()
    plc_unit=models.IntegerField()

    def __unicode__(self):
          return self.name

    class Admin: pass
 
class VoipGateway(models.Model):
    name=models.OneToOneField('sipconfig.station')
    def __unicode__(self):
          return self.name.dev_name
    class Admin: pass
    
class Extension(models.Model):
    PREFIX_CHOICE=(
        ('1','station'),
        ('9','lock'),
        ('8','voicemail'))
    context=models.ForeignKey('sipconfig.station')
    plc_sys=models.ForeignKey(Plc)
    gateway=models.ForeignKey(VoipGateway)
    word=models.IntegerField()
    bit=models.IntegerField()
    prefix=models.CharField(max_length=2,choices=PREFIX_CHOICE)
    ph_number=models.CharField(max_length=8)
    
    def __unicode__(self):
          return self.context.dev_name +'-'+ self.ph_number

    class Admin: pass

class ExtensionForm(ModelForm):
    class Meta:
       model = Extension
            
        
class ExtAddToContextForm(ModelForm):
    word_increment=IntegerField(widget=TextInput,initial=0)
    bit_increment=IntegerField(widget=TextInput,initial=0)
    phone_increment = IntegerField(widget=TextInput,initial=0)
    class Meta:
        model = Extension
class PlcForm(ModelForm):
    class Meta:
        model=Plc
class VoipGatewayForm(ModelForm):
    class Meta:
        model=VoipGateway
-------------------------------- with this admin.py -------------------------------
from sipprovision.extensions.models import Plc,VoipGateway,Extension
from django.contrib import admin
class ExtensionAdmin(admin.ModelAdmin):
    list_display=('context','plc_sys','gateway','word','bit','prefix','ph_number')
    list_filter=('plc_sys','prefix')
class PlcAdmin(admin.ModelAdmin):
    list_display=('name','ip_addr','ip_port','plc_node','plc_net','plc_unit')
admin.site.register(Plc,PlcAdmin)
admin.site.register(VoipGateway)
admin.site.register(Extension,ExtensionAdmin)

------------------- LINE in related.py returning error is ------------------------
 def formfield(self, **kwargs):
        defaults = {
            'form_class': forms.ModelChoiceField,
            'queryset': self.rel.to._default_manager.complex_filter(
                                                    self.rel.limit_choices_to),
            'to_field_name': self.rel.field_name,
        }
        defaults.update(kwargs)
        return super(ForeignKey, self).formfield(**defaults)

Attachments (2)

10405.patch (3.4 KB ) - added by Jonas Obrist 13 years ago.
Raise a more useful exception when this issue occurs
10405.2.patch (3.4 KB ) - added by Chris Wesseling 13 years ago.
Fixed typo, spotted by GDorn.

Download all attachments as: .zip

Change History (49)

comment:1 by Alex Gaynor, 16 years ago

Description: modified (diff)

Pleas use the preview button. Further this is tested quite extensively: http://code.djangoproject.com/browser/django/trunk/tests/regressiontests/string_lookup/models.py so if no more details can be provided I'm going to mark worksforme.

comment:2 by Ramiro Morales, 16 years ago

See also #8569

comment:3 by Karen Tracey, 16 years ago

Replying to Alex:

Pleas use the preview button. Further this is tested quite extensively: http://code.djangoproject.com/browser/django/trunk/tests/regressiontests/string_lookup/models.py so if no more details can be provided I'm going to mark worksforme.

Notice two people have reported this recently on django-users, both (I believe) mentioned they saw the problem only under mod_python/Apache, and not the development server, so there is something different about non-dev-server environment. I'm not sure the test environment can be used to completely mimic what's happening under Apache here. I could wish for a more minimal failing example, but before closing worksforme I think something like this needs to be tested under mod_python (or mod_wsgi), not just under dev server. I myself won't be able to do that for a few days yet, as I'm away from home and my test machines. If someone else does do that please post the minimal (failing or working) example of FK reference using quoted strings.

Replying to ramiro:

See also #8569

One of the posters who ran into this on the user's list did find that ticket also. The fix for that went in before 1.0 so I believe both posters already have it. That fix happens to only be relevant when DEBUG is set to True: the traceback there was from the admin validation routine which is only called when DEBUG is on, and the fix that went in also applies only when DEBUG is on. At least one of the posters who ran into this recently specifically noted it only happened with DEBUG=False, and under Apache, not the dev server.

comment:4 by Jacob, 16 years ago

milestone: 1.1
Triage Stage: UnreviewedAccepted

comment:5 by Ramiro Morales, 16 years ago

I have been able to reproduce this with Django trunk r9984 + mod_python + Apache with this minimal setup:

A t10405 app:

# models.py
from django.db import models
from django.forms import ModelForm

class Extension(models.Model):
    context=models.ForeignKey('app2.station')

class ExtensionForm(ModelForm):
    class Meta:
        model = Extension
# admin.py
from t10405.models import Extension
from django.contrib import admin

admin.site.register(Extension)

The 'app2' application has only a models.py file:

# models.py
from django.db import models

class station(models.Model):
    name = models.CharField(max_length=10)

urls.py has only the admin app activated (no app URLs).

Bug shows itself when accessing the main admin page and shows as a pure text traceback when DEBUG= False.

Note how t10405's admin.py imports models.py and this one contains a ModelForm -derived class (this is also visible in the OP's traceback). Key thing here for the bug appearing is the ExtensionForm's Meta inner-class definition because replacing the ExtensionForm body with pass or completely commenting out ExtensionForm so only the ModelForm import is left makes the bug disappear.

The traceback:

Mod_python error: "PythonHandler django.core.handlers.modpython"

Traceback (most recent call last):

  File "/usr/lib/python2.4/site-packages/mod_python/apache.py", line 299, in HandlerDispatch
    result = object(req)

  File "/home/ramiro/src/django/trunk/django/core/handlers/modpython.py", line 228, in handler
    return ModPythonHandler()(req)

  File "/home/ramiro/src/django/trunk/django/core/handlers/modpython.py", line 201, in __call__
    response = self.get_response(request)

  File "/home/ramiro/src/django/trunk/django/core/handlers/base.py", line 128, in get_response
    return self.handle_uncaught_exception(request, resolver, exc_info)

  File "/home/ramiro/src/django/trunk/django/core/handlers/base.py", line 159, in handle_uncaught_exception
    callback, param_dict = resolver.resolve500()

  File "/home/ramiro/src/django/trunk/django/core/urlresolvers.py", line 226, in resolve500
    return self._resolve_special('500')

  File "/home/ramiro/src/django/trunk/django/core/urlresolvers.py", line 215, in _resolve_special
    callback = getattr(self.urlconf_module, 'handler%s' % view_type)

  File "/home/ramiro/src/django/trunk/django/core/urlresolvers.py", line 200, in _get_urlconf_module
    self._urlconf_module = __import__(self.urlconf_name, {}, {}, [''])

  File "/web/djapps/dtests/urls.py", line 2, in ?
    from t10405.models import Extension

  File "/web/djapps/dtests/t10405/models.py", line 7, in ?
    class ExtensionForm(ModelForm):

  File "/home/ramiro/src/django/trunk/django/forms/models.py", line 195, in __new__
    opts.exclude, formfield_callback)

  File "/home/ramiro/src/django/trunk/django/forms/models.py", line 162, in fields_for_model
    formfield = formfield_callback(f)

  File "/home/ramiro/src/django/trunk/django/forms/models.py", line 177, in 
    lambda f: f.formfield())

  File "/home/ramiro/src/django/trunk/django/db/models/fields/related.py", line 700, in formfield
    defaults = {

AttributeError: 'str' object has no attribute '_default_manager'

It's worth noting that the other user reporting this on django-users (http://groups.google.com/group/django-users/browse_frm/thread/ae22023a61290c1e) also defined a ModelForm subclass but in an admin.py file.

Trying to execute syncdb also shows the bug (even with DEBUG = False) but with a different code path:

$ python manage.py syncdb
Traceback (most recent call last):
  File "manage.py", line 11, in ?
    execute_manager(settings)
  File "/home/ramiro/src/django/trunk/django/core/management/__init__.py", line 350, in execute_manager
    utility.execute()
  File "/home/ramiro/src/django/trunk/django/core/management/__init__.py", line 295, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/ramiro/src/django/trunk/django/core/management/base.py", line 195, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/home/ramiro/src/django/trunk/django/core/management/base.py", line 221, in execute
    self.validate()
  File "/home/ramiro/src/django/trunk/django/core/management/base.py", line 249, in validate
    num_errors = get_validation_errors(s, app)
  File "/home/ramiro/src/django/trunk/django/core/management/validation.py", line 28, in get_validation_errors
    for (app_name, error) in get_app_errors().items():
  File "/home/ramiro/src/django/trunk/django/db/models/loading.py", line 128, in get_app_errors
    self._populate()
  File "/home/ramiro/src/django/trunk/django/db/models/loading.py", line 57, in _populate
    self.load_app(app_name, True)
  File "/home/ramiro/src/django/trunk/django/db/models/loading.py", line 72, in load_app
    mod = __import__(app_name, {}, {}, ['models'])
  File "/web/djapps/dtests/t10405/models.py", line 7, in ?
    class ExtensionForm(ModelForm):
  File "/home/ramiro/src/django/trunk/django/forms/models.py", line 195, in __new__
    opts.exclude, formfield_callback)
  File "/home/ramiro/src/django/trunk/django/forms/models.py", line 162, in fields_for_model
    formfield = formfield_callback(f)
  File "/home/ramiro/src/django/trunk/django/forms/models.py", line 177, in <lambda>
    lambda f: f.formfield())
  File "/home/ramiro/src/django/trunk/django/db/models/fields/related.py", line 700, in formfield
    defaults = {
AttributeError: 'str' object has no attribute '_default_manager'

comment:6 by Ramiro Morales, 16 years ago

Cc: Ramiro Morales added

comment:7 by Jacob, 16 years ago

Component: UncategorizedDatabase layer (models, ORM)

comment:8 by Sam Thompson, 16 years ago

Confirming that happens to me as well in the 1.1 beta 1, freshly installed. I also can show an identical case (except for declaration order) which doesn't cause the problem.

This arrangement causes the AttributeError:

class ClassA(models.Model):
    ref = models.ForeignKey("ClassB")

class ClassAForm(ModelForm):
    class Meta:
        model=ClassA

class ClassB(models.Model):
    pass

This arrangement does not:

class ClassA(models.Model):
    ref = models.ForeignKey("ClassB")

class ClassB(models.Model):
    pass

class ClassAForm(ModelForm):
    class Meta:
        model=ClassA

Hope this helps.

comment:9 by Armin Ronacher, 16 years ago

Owner: changed from nobody to Armin Ronacher

comment:10 by Jacob, 16 years ago

milestone: 1.11.2

The good news is that there's an easy workaround, so we can punt this to the 1.2 release. For now, just make sure you define ModelForms after Models.

The bad news is that this is going to be very hard to fix.

comment:11 by mkibbel, 16 years ago

I've been having a similar problem recently with one of my projects. The AttributeError gets raised inside the call to admin.autodiscover() in my urls.py, similar to bugs described in #8569. Due to model interdependencies, I have to to use lazy model referencing with quoted strings for some of the FK and M2M relationships in my project.

I never ran into this AttributeError until I turned DEBUG off. After reading through these tickets and pouring through some of the Django source code, I came up with a simple workaround for my particular problem. Just add this to urls.py before calling admin.autodiscover():

from django.db.models.loading import cache as model_cache
if not model_cache.loaded:
    model_cache.get_models()

My reasoning for this is explained briefly in the next comment.

comment:12 by mkibbel, 16 years ago

In admin.autodiscover(), the AttributeError is occurring when a model form class is being created when Django translates django.forms field objects for each of the django.db.models field objects defined in the model (see the django.forms.models.ModelFormMetaclass.__new__() function). This process ends up calling each model field's formfield() method to perform the translation.

The formfield() implementation in ForeignKey, OneToOneField and ManyToManyField each refer to self.rel.to. Until all of the models are completely loaded and lazy model references are resolved, self.rel.to may still be a string and not a model class. The change from a string to a model class object happens in a lazy callback set up in django.db.models.fields.related.RelatedField.contribute_to_class():

other = self.rel.to # NOTE: still a string for lazy model references
if isinstance(other, basestring):
    def resolve_related_class(field, model, cls):
        field.rel.to = model
        field.do_related_class(model, cls)
    add_lazy_relation(cls, self, other, resolve_related_class)
else:
    self.do_related_class(other, cls)

See the definition of add_lazy_relation() and do_pending_lookups(), the signals.class_prepared handler, in this module for more details.

You have to prevent a field's self.rel.to attribute from being used until after lazy model resolution for its model is complete. By using the model cache API defined in django.db.models.loading, you can guarantee that all models are loaded before admin.autodiscover() loads and registers the admin classes.

One simple change to admin.autodiscover() could resolve this problem, but I'm not sure what other implications it would have. autodiscover() searches for admin.py modules by importing each application module defined in settings.INSTALLED_APPS and checking if an admin module exists. This code could use the model caching API instead to retrieve the application modules, so we'd be replacing:

for app in settings.INSTALLED_APPS:
    # process apps (strings)


with:

for app in model_cache.get_apps():
    # process apps (module objects)

get_apps() loads all models before returning, so we can be sure that all the models are ready before any of the admin imports happen. The admin class creation process will load all the model classes, so I don't think it would hurt performance to pre-load them. It is possible that this change would cause other unexpected side effects in some projects.

comment:13 by Karen Tracey, 16 years ago

#11143 reported this again.

comment:15 by ikuta85@…, 15 years ago

Version: 1.0-alpha-21.1

In 1.1 version, I still got the same error after I follow the solution http://code.djangoproject.com/ticket/10405#comment:11

in reply to:  15 comment:16 by Armin Ronacher, 15 years ago

Replying to ikuta85@gmail.com:

In 1.1 version, I still got the same error after I follow the solution http://code.djangoproject.com/ticket/10405#comment:11

That solution is a no-solution. The problem is a technical limitation of the Django/Python combination and very hard to fix. The correct workaround is to change the dependencies between forms and classes that access happens after the model was finalized.

comment:17 by anonymous, 15 years ago

Cc: jamespic@… added

comment:18 by anonymous, 15 years ago

Cc: subsume@… added

comment:19 by anonymous, 15 years ago

Cc: tom@… added

comment:20 by Karen Tracey, 15 years ago

milestone: 1.2

Given we still don't have any better fix than "don't do that", this isn't going to make 1.2.

comment:21 by Camilo Nova, 15 years ago

I just realize that this error doesnt happen when i use a file named admin.py. This file contains all the admin related, extracting it from models.py you should check if this solves all kinds of problems related to this ticket. There's no need to make any changes to urls.py as well, just using the admin.py file works great for me.

comment:22 by bernhard, 14 years ago

Version: 1.11.2

I experience the same problem when trying to create a ModelFrom for an abstract Model, having a ForeignKey('self')!

comment:23 by stormlifter, 14 years ago

I was able to fix my error by removing a ForeignKey reference in the model that was being used in the ModelForm from the ModelForm using "exclude" in the Meta.

comment:24 by Eduardo Cereto, 14 years ago

Cc: eduardocereto@… added

comment:25 by Chris Beaven, 14 years ago

Severity: Normal
Type: Bug

comment:26 by tehfink <djsnickles@…>, 14 years ago

Cc: djsnickles@… added

comment:27 by tehfink <djsnickles@…>, 14 years ago

Interestingly, I've noticed this AttributeError is not raised if the quoted class name passed to the ForeignKey is 'auth.User'…perhaps because 'django.contrib.auth' is usually the first installed app in INSTALLED_APPS?

comment:28 by Camilo Nova, 14 years ago

Version: 1.21.3
Version 0, edited 14 years ago by Camilo Nova (next)

comment:29 by anonymous, 14 years ago

Easy pickings: unset

comment:30 by michaelvantellingen@…, 14 years ago

Cc: michaelvantellingen@… added
Easy pickings: set

Sorry, i meant to add myself as cc as I just encountered this issue on django 1.3

by Jonas Obrist, 13 years ago

Attachment: 10405.patch added

Raise a more useful exception when this issue occurs

comment:31 by Jonas Obrist, 13 years ago

Easy pickings: unset
Has patch: set
Keywords: dceu2011 added
UI/UX: unset

Fixed this by raising a more useful exception, see patch. Discussed this with Alex and Andrew and they agreed that this is a reasonable solution for this.

comment:32 by Harro, 13 years ago

Tested the patch, applies to trunk and tests pass.

comment:33 by Chris Wesseling, 13 years ago

Triage Stage: AcceptedReady for checkin

Not only do the test pass, they fail when applied without the solution.

All different code-paths I've seen in the reports end in the place of the solution.

So RFC imho.

comment:34 by Camilo Nova, 13 years ago

Seems to work fine. Great job.

comment:35 by Sam Thompson, 13 years ago

Small grammar nitpick:

"it's related model %r has not been loaded yet" % (self.name, self.rel.to))

Should be:

"its related model %r has not been loaded yet" % (self.name, self.rel.to))

by Chris Wesseling, 13 years ago

Attachment: 10405.2.patch added

Fixed typo, spotted by GDorn.

comment:36 by funvit@…, 13 years ago

got that in 1.3

comment:37 by Jannis Leidel, 13 years ago

Resolution: fixed
Status: newclosed

In [16604]:

Fixed #10405 -- Raise a more useful error if the formfield of a related model field can't be created yet because the related model isn't loaded yet. Thanks ojii and charstring.

comment:38 by ionalchemist, 13 years ago

I initially put my ModelForms after each respective model and encountered this error. After reading https://code.djangoproject.com/ticket/10405#comment:12, that prompted me to move all of my ModelForms to the end of my models.py file and voila. Easy fix.

comment:39 by Simon Litchfield, 12 years ago

Patch needs improvement: set
Version: 1.3

This fix seems to have reverted- ie I'm getting the old str error. I'm on c4c7fbcc0d9264beb931b45969fc0d8d655c4f83

comment:40 by Jeremy Blanchard, 12 years ago

I'm also experiencing this in 1.4.1. I can confirm this regression.

in reply to:  40 comment:41 by anonymous, 12 years ago

Replying to auzigog:

I'm also experiencing this in 1.4.1. I can confirm this regression.

Can you post a simple example demonstrating the issue?

comment:42 by Sergio Oliveira, 12 years ago

Still happening to me too.
I'm using Django 1.3.3 and the steps to reproduce are the same mentioned in comment 8.

comment:43 by Sergio Oliveira, 12 years ago

Cc: seocam@… added

comment:44 by fc@…, 12 years ago

I just got this error with Django 1.4.2, mod_wsgi and Apache

comment:45 by Camilo Nova, 12 years ago

I got this same error too with Django 1.4.2, uwsgi and nginx

Maybe the changes were reverted or something?

comment:46 by Ramiro Morales, 12 years ago

I've just tested this with a setup like comment:42 suggest: The one pasted in comment:8. I did it with both trunk as of now and 1.4.2. The problematic setup doesn't even allow me to run syncdb. If I change the models/modelform ordering to the one that works, run syncdb and then change it back to the problematic one the error message introduced with the fix for this ticket is shown inmediately when running the dev server. The message is always:

ValueError: Cannot create form field for 'ref' yet, because its related model 'ClassB' has not been loaded yet

I tested with DEBUG set both to True and False.

Guys, you will need to provide much more detail, as in a simple setup that demonstrates the purported regression if you want anyone to dedicate any more time to this. Also, please open a new ticket for that, commenting on a closed ticket isn't a good way to get things going either.

comment:47 by Gwildor Sok, 10 years ago

For anyone else who ran into this: for me this occurred because I had a form import in my main urls.py file, to pass a form to a built in view by Django. That forms.py file had a ModelForm and the model it was associated with had a foreign key using a string for the model (because of earlier import errors). This didn't happen on 1.4, but it occurred when I upgraded Django to 1.5. I fixed this by moving my form import line beneath the admin.autodiscover() line.

Last edited 10 years ago by Gwildor Sok (previous) (diff)
Note: See TracTickets for help on using tickets.
Back to Top