djangodjango-modelsdjango-adminforeign-keyssatchmo

fk_name 'user' is not a ForeignKey to <class 'satchmo_store.contact.models.Contact'>


I am using django 1.3.1 and satchmo 0.9.2. I used the default model that ships with Satchmo called Contact. I created a satchmo_mod app, and an admin.py file.

pip install django==1.3.1
pip install -r http://bitbucket.org/chris1610/satchmo/raw/tip/scripts/requirements.txt
pip install satchmo 0.9.2

django-admin.py startproject fk_test
cd fk_test
python manage.py startapp satchmo_mod

then create admin.py:

from satchmo_store.contact.models import Contact
admin.site.unregister(Contact)
admin.site.register(Contact)

I then run:

python manage.py runserver

Go to:

127.0.0.1:8000

Get this error:

fk_name 'user' is not a ForeignKey to <class 'satchmo_store.contact.models.Contact'>

I see this error in the stack trace and start to go exploring:

/home/cody/work/martin-instruments/virtual-envs/mi-prod-copy/lib/python2.6/site-packages/django/contrib/admin/validation.py in validate_inline
    fk = _get_foreign_key(parent_model, cls.model, fk_name=cls.fk_name, can_fail=True) ...
▼ Local vars
Variable    Value
parent_model    
<class 'satchmo_store.contact.models.Contact'>
cls 
<class 'satchmo_mod.admin.UserTaxExemptInline'>
parent  
<class 'django.contrib.admin.options.ModelAdmin'>
f   
<django.db.models.fields.related.OneToOneField object at 0x2ec2250>

Long story short, when the Contact model gets registered back, all of it's _meta options aren't being regenerated as far as I can tell. See the 'manage.py shell' session below:

envs/mi2.0/mi$ ./manage.py shell
Python 2.6.6 (r266:84292, Dec 26 2010, 22:31:48) 
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from satchmo_mod.admin import UserTaxExemptInline
>>> from satchmo_mod.admin import MyContactOptions
>>> from django.db.models.fields.related import OneToOneField
>>> from satchmo_store.contact.models import Contact
>>> cls = UserTaxExemptInline
>>> parent_model = Contact
>>> parent = MyContactOptions
>>> from django.contrib.admin.validation import get_field
>>> f = get_field(cls, cls.model, cls.model._meta, 'fk_name', cls.fk_name)
>>> print f
<django.db.models.fields.related.OneToOneField object at 0x2c358d0>
>>> dir(f)
['__class__', '__cmp__', '__deepcopy__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__metaclass__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slotnames__', '__str__', '__subclasshook__', '__weakref__', '_choices', '_description', '_get_choices', '_get_flatchoices', '_get_val_from_obj', '_pk_trace', '_unique', 'attname', 'auto_created', 'auto_creation_counter', 'bind', 'blank', 'choices', 'clean', 'column', 'contribute_to_class', 'contribute_to_related_class', 'creation_counter', 'db_column', 'db_index', 'db_tablespace', 'db_type', 'default', 'default_error_messages', 'default_validators', 'description', 'do_related_class', 'editable', 'empty_strings_allowed', 'error_messages', 'flatchoices', 'formfield', 'get_attname', 'get_attname_column', 'get_cache_name', 'get_choices', 'get_choices_default', 'get_db_prep_lookup', 'get_db_prep_save', 'get_db_prep_value', 'get_default', 'get_flatchoices', 'get_internal_type', 'get_prep_lookup', 'get_prep_value', 'get_validator_unique_lookup_type', 'has_default', 'help_text', 'max_length', 'model', 'name', 'null', 'opts', 'pre_save', 'primary_key', 'rel', 'related', 'related_query_name', 'run_validators', 'save_form_data', 'serialize', 'set_attributes_from_name', 'set_attributes_from_rel', 'to_python', 'unique', 'unique_for_date', 'unique_for_month', 'unique_for_year', 'validate', 'validators', 'value_from_object', 'value_to_string', 'verbose_name']
>>> from django.db import models
>>> isinstance(f, models.ForeignKey)
True
>>> from django.forms.models import _get_foreign_key
>>> fk = _get_foreign_key(parent_model, cls.model, fk_name=cls.fk_name, can_fail=True)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/home/cody/work/martin-instruments/virtual-envs/mi2.0/lib/python2.6/site-packages/django/forms/models.py", line 770, in _get_foreign_key
    raise Exception("fk_name '%s' is not a ForeignKey to %s" % (fk_name, parent_model))
Exception: fk_name 'user' is not a ForeignKey to <class 'satchmo_store.contact.models.Contact'>
>>> print parent_model, cls.model, cls.fk_name
<class 'satchmo_store.contact.models.Contact'> <class 'satchmo_mod.models.UserTaxExempt'> user
>>> fk = _get_foreign_key(parent_model, cls.model, fk_name=cls.fk_name, can_fail=True)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/home/cody/work/martin-instruments/virtual-envs/mi2.0/lib/python2.6/site-packages/django/forms/models.py", line 770, in _get_foreign_key
    raise Exception("fk_name '%s' is not a ForeignKey to %s" % (fk_name, parent_model))
Exception: fk_name 'user' is not a ForeignKey to <class 'satchmo_store.contact.models.Contact'>
>>> print model
Traceback (most recent call last):
  File "<console>", line 1, in <module>
NameError: name 'model' is not defined
>>> print parent_model
<class 'satchmo_store.contact.models.Contact'>
>>> cls.model
<class 'satchmo_mod.models.UserTaxExempt'>
>>> model = cls.model
>>> opts = model._meta
>>> fk_name
Traceback (most recent call last):
  File "<console>", line 1, in <module>
NameError: name 'fk_name' is not defined
>>> fk_name = cls.fk_name
>>> fk_name
'user'
>>> fks_to_parent = [f for f in opts.fields if f.name == fk_name]
>>> print fks_to_parent
[<django.db.models.fields.related.OneToOneField object at 0x2c358d0>]
>>> not isinstance(fk, ForeignKey)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
NameError: name 'fk' is not defined
>>> fk = fks_to_parent[0]
>>> not isinstance(fk, ForeignKey)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
NameError: name 'ForeignKey' is not defined
>>> from django.db.models import ForeignKey
>>> not isinstance(fk, ForeignKey)
False
>>> len(fks_to_parent) == 1
True
>>> not isinstance(fk, ForeignKey) or \
...  768                     (fk.rel.to != parent_model and
... fk.rel.to not in parent_model._meta.get_parent_list())
Traceback (most recent call last):
  File "<console>", line 3, in <module>
TypeError: 'int' object is not callable
>>> not isinstance(fk, ForeignKey) or (fk.rel.to != parent_model and fk.rel.to not in parent_model._meta.get_parent_list())
True
>>> parent_model
<class 'satchmo_store.contact.models.Contact'>
>>> parent_model._meta.get_parent_list()
set([])
>>> fk.rel.to
<class 'django.contrib.auth.models.User'>
>>> fk.rel.to != parent_model
True
>>> fk.rel.to not in parent_model._meta.get_parent_list()
True
>>> fk.rel.to
<class 'django.contrib.auth.models.User'>
>>> fk.rel.to == parent_model
False
>>> parent_model
<class 'satchmo_store.contact.models.Contact'>
>>> fk
<django.db.models.fields.related.OneToOneField object at 0x2c358d0>
>>> fk.rel.to
<class 'django.contrib.auth.models.User'>
>>> # User has to be not equal to Contact
>>> # and fk.rel.to can't be in the parent model's parent list
>>> fk.rel.to
<class 'django.contrib.auth.models.User'>
>>> fk.rel
<django.db.models.fields.related.OneToOneRel object at 0x2c35990>
>>> fk.rel.to != parent_model
True
>>> # OneToOneRel can't be equal to parent_model(Contact) nor can OneToOneRel be in the parent_model(Contact) parent list
>>> Contact._meta.get_parent_list()
set([])
>>> parent_model is Contact
True
>>> fk.rel.to in parent_model._meta.get_parent_list()
False
>>> fk.rel.to != parent_model
True
>>> (fk.rel.to != parent_model and fk.rel.to not in parent_model._meta.get_parent_list())
True
>>> (fk.rel.to != parent_model and fk.rel.to not in parent_model._meta.get_parent_list())
True
>>> (True and False)
False
>>> fk.rel.to != parent_model
True
>>> fk.rel.to.not in parent_model._meta.get_parent_list()
  File "<console>", line 1
    fk.rel.to.not in parent_model._meta.get_parent_list()
                ^
SyntaxError: invalid syntax
>>> fk.rel.to not in parent_model._meta.get_parent_list()
True
>>> fk.rel.to != parent_model
True
>>> fk.rel.to not in parent_model._meta.get_parent_list()
True
>>> fk.rel.to
<class 'django.contrib.auth.models.User'>
>>> parent-model
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: unsupported operand type(s) for -: 'MediaDefiningClass' and 'ModelBase'
>>> parent_model
<class 'satchmo_store.contact.models.Contact'>
>>> fk.rel.to
<class 'django.contrib.auth.models.User'>
>>> parent_model
<class 'satchmo_store.contact.models.Contact'>
>>> parent_model._meta.get_parent_list()
set([])
>>> parent_model.parents
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: type object 'Contact' has no attribute 'parents'
>>> parent_model.options
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: type object 'Contact' has no attribute 'options'
>>> opts
<Options for UserTaxExempt>
>>> parent_model._meta
<Options for Contact>
>>> parent_model._meta.parents
{}
>>> User._meta.parents
Traceback (most recent call last):
  File "<console>", line 1, in <module>
NameError: name 'User' is not defined
>>> from django.contrib.auth.models import User
>>> User._meta.parents
{}
>>> from martin.models import CreditApplication
>>> CreditApplication._meta.parents
{}
>>> User._meta.fields
[<django.db.models.fields.AutoField object at 0x21d45d0>, <django.db.models.fields.CharField object at 0x21d2450>, <django.db.models.fields.CharField object at 0x21d25d0>, <django.db.models.fields.CharField object at 0x21d26d0>, <django.db.models.fields.EmailField object at 0x21d27d0>, <django.db.models.fields.CharField object at 0x21d2950>, <django.db.models.fields.BooleanField object at 0x21d2a90>, <django.db.models.fields.BooleanField object at 0x21d2bd0>, <django.db.models.fields.BooleanField object at 0x21d2d10>, <django.db.models.fields.DateTimeField object at 0x21d2e10>, <django.db.models.fields.DateTimeField object at 0x21d2e90>]
>>> Contact._meta.fields
[<django.db.models.fields.AutoField object at 0x289a510>, <django.db.models.fields.CharField object at 0x2899a90>, <django.db.models.fields.CharField object at 0x2899c10>, <django.db.models.fields.CharField object at 0x2899d10>, <django.db.models.fields.related.ForeignKey object at 0x2899dd0>, <django.db.models.fields.related.ForeignKey object at 0x2899e90>, <django.db.models.fields.related.ForeignKey object at 0x2899f90>, <django.db.models.fields.DateField object at 0x289a0d0>, <django.db.models.fields.EmailField object at 0x289a150>, <django.db.models.fields.TextField object at 0x289a2d0>, <django.db.models.fields.DateField object at 0x289a350>]
>>> Contact._meta.fields[0]
<django.db.models.fields.AutoField object at 0x289a510>
>>> dir(Contact._meta.fields[0])
['__class__', '__cmp__', '__deepcopy__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__metaclass__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_choices', '_description', '_get_choices', '_get_flatchoices', '_get_val_from_obj', '_unique', 'attname', 'auto_created', 'auto_creation_counter', 'bind', 'blank', 'choices', 'clean', 'column', 'contribute_to_class', 'creation_counter', 'db_column', 'db_index', 'db_tablespace', 'db_type', 'default', 'default_error_messages', 'default_validators', 'description', 'editable', 'empty_strings_allowed', 'error_messages', 'flatchoices', 'formfield', 'get_attname', 'get_attname_column', 'get_cache_name', 'get_choices', 'get_choices_default', 'get_db_prep_lookup', 'get_db_prep_save', 'get_db_prep_value', 'get_default', 'get_flatchoices', 'get_internal_type', 'get_prep_lookup', 'get_prep_value', 'get_validator_unique_lookup_type', 'has_default', 'help_text', 'max_length', 'model', 'name', 'null', 'pre_save', 'primary_key', 'rel', 'run_validators', 'save_form_data', 'serialize', 'set_attributes_from_name', 'to_python', 'unique', 'unique_for_date', 'unique_for_month', 'unique_for_year', 'validate', 'validators', 'value_from_object', 'value_to_string', 'verbose_name']
>>> Contact._meta.fields[0].model
<class 'satchmo_store.contact.models.Contact'>
>>> Contact._meta.fields[1].model
<class 'satchmo_store.contact.models.Contact'>

UPDATE: I did the fix recommended by Maccesch. But it seems after unregistering the Contact object, and reregistering it with the new inline, it did something to break Zinnia:

fk_name 'user' is not a ForeignKey to <class 'zinnia.models.Category'>

UPDATE: Might want to start a new question, wasn't sure.

This is the code for the model and modeladmin:

Models.py

class UserTaxExempt(models.Model):
    user = models.OneToOneField(_User, primary_key=True)
    tax_exempted = models.BooleanField("No taxes would be applied to purchases")

    class Meta:
        verbose_name = _('Tax Exemption')
        verbose_name_plural = _('Tax Exemption') 

    def __unicode__(self):
        if self.tax_exempted:
            return unicode("Purchases are exempted from taxes")
        else:
            return unicode("Purchases are taxed")

admin.py

from satchmo_mod.models import ContactTaxExempt
from satchmo_store.contact.admin import PhoneNumber_Inline, AddressBook_Inline
from satchmo_store.contact.models import Contact

class ContactTaxExemptInline(admin.TabularInline):
    model = ContactTaxExempt
    max_num = 1 
    extra = 1 
    can_delete = False
    fk_name = "user"

class ContactOptions(admin.ModelAdmin):
    list_display = ('last_name', 'first_name')
    list_filter = ['create_date']
    ordering = ['last_name']
    search_fields = ('first_name', 'last_name', 'email')
    related_search_fields = {'user': ('username', 'first_name', 'last_name', 'em
    related_string_functions = {'user': lambda u: u"%s (%s)" % (u.username, u.ge
    inlines = [ContactTaxExemptInline, PhoneNumber_Inline, AddressBook_Inline]

admin.site.unregister(Contact)
admin.site.register(Contact, ContactOptions)

So UserTaxExempt has a foreignkey to User, so shouldn't this work just fine? It works on the User page, so I'm not getting why it wouldn't work on the Contact page.


Solution

  • The problem seems to be in your UserTaxExemptInline. You didn't post how that looks like but I guess it looks like this:

    class UserTaxExemptInline(admin.TabularInline):
        model = Contact
        fk_name = "user"
    

    while it should look like this:

    class UserTaxExemptInline(admin.TabularInline):
        model = User