#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 )
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)
Change History (49)
comment:1 by , 16 years ago
Description: | modified (diff) |
---|
comment:3 by , 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 , 16 years ago
milestone: | → 1.1 |
---|---|
Triage Stage: | Unreviewed → Accepted |
comment:5 by , 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 , 16 years ago
Cc: | added |
---|
comment:7 by , 16 years ago
Component: | Uncategorized → Database layer (models, ORM) |
---|
comment:8 by , 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 , 16 years ago
Owner: | changed from | to
---|
comment:10 by , 16 years ago
milestone: | 1.1 → 1.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 , 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 , 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:14 by , 15 years ago
For me this solves the error http://code.djangoproject.com/ticket/10405#comment:11
follow-up: 16 comment:15 by , 15 years ago
Version: | 1.0-alpha-2 → 1.1 |
---|
In 1.1 version, I still got the same error after I follow the solution http://code.djangoproject.com/ticket/10405#comment:11
comment:16 by , 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 , 15 years ago
Cc: | added |
---|
comment:18 by , 15 years ago
Cc: | added |
---|
comment:19 by , 15 years ago
Cc: | added |
---|
comment:20 by , 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 , 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 , 14 years ago
Version: | 1.1 → 1.2 |
---|
I experience the same problem when trying to create a ModelFrom for an abstract Model, having a ForeignKey('self')!
comment:23 by , 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 , 14 years ago
Cc: | added |
---|
comment:25 by , 14 years ago
Severity: | → Normal |
---|---|
Type: | → Bug |
comment:26 by , 14 years ago
Cc: | added |
---|
comment:27 by , 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 , 14 years ago
Version: | 1.2 → 1.3 |
---|
I recently have this error too using django 1.3. I have defined ModelForms in my views.py and i was importing them in my urls.py so that could be the cause of the problem. Later i try to change my generic views to class based views, but still have the problem because in my views.py where i defined my class based views i also import the ModelForms and it stills give me this error, so changing to class based views was not the solution.
At IRC someone tell me to try to declare my views as strings instead of importing them in my urls.py (not working with class based views) and that solves the problem, because when reading the urls.py there is no direct imports from views.py.
The problem could be when the ModelForm class is getting the information about the model.
Reading all the comments and workarounds does not work for me.
I dont have admin.py and the only place where i import ModelForms is at views.py
comment:29 by , 14 years ago
Easy pickings: | unset |
---|
comment:30 by , 14 years ago
Cc: | added |
---|---|
Easy pickings: | set |
Sorry, i meant to add myself as cc as I just encountered this issue on django 1.3
by , 13 years ago
Attachment: | 10405.patch added |
---|
Raise a more useful exception when this issue occurs
comment:31 by , 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:33 by , 13 years ago
Triage Stage: | Accepted → Ready 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:35 by , 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))
comment:38 by , 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 , 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
follow-up: 41 comment:40 by , 12 years ago
I'm also experiencing this in 1.4.1. I can confirm this regression.
comment:41 by , 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 , 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 , 12 years ago
Cc: | added |
---|
comment:45 by , 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 , 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 , 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.
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.