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

2

6

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.

Lurk answered 19/6, 2012 at 5:19 Comment(8)
What exactly are you trying to do, and how isn't it working? The only time I see the error appear is when you're calling Django-internal functions, and I have no idea what I'm looking at in the rest of that console output.Resnick
I am trying to register a simple ModelAdmin. I took out the modeladmin and just unregistered the model, re-registered it, and it gave me the error again! The console output is me digging down to the validate function used to validate foreign keys and looking at all the values/etc. It seems that _meta.get_parents_list() isn't being generated correctly. When I take out the foreign key validation in the actual django files (bad, I know but testing it to see if works), the site works fine. It is obviously a valid foreign key. Does satchmo do some weird edits to django that messes this up?Lurk
As Elliott said it still isn't clear what you really are trying to do. The Contact model in your question is this the one from satchmo_store.contact.models? And what do you mean with register a simple ModelAdmin? Please update your question and give more context.Opuscule
Please, try to make your question clearer so we can help you with your problem. Do not put just the error you are getting. Try to explain what you are trying to do too. If the problem is with an admin model, add that model here. Add the line in which you unregistered the admin model... give us more clues so we can help you faster and betterOsset
I didn't add a model admin. I simply registered the default Contact model that ships with satchmo and registered it again. It doesn't register correctly or something, because the Contact._meta.get_parent_list() function doesn't properly generatethings.Lurk
I have updated the question and simplified everything into the single problem. I also provided all needed information to replicate the problem yourself.Lurk
In your INSTALLED_APPS is 'satchmo' above 'satchmo_mod'?Opuscule
There is no satchmo, but satchmo_util and satchmo_store are above satchmo_mod, yes.Lurk
O
4

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
Opuscule answered 26/6, 2012 at 8:21 Comment(1)
It still is the right solution. You should maybe ask a new question about that zinnia problem?Opuscule
A
1

See _get_foreign_key in django/forms/models.py.

_get_foreign_key description:

Finds and returns the ForeignKey from model to parent if there is one
(returns None if can_fail is True and no such field exists). If fk_name is
provided, assume it is the name of the ForeignKey field. Unles can_fail is
True, an exception is raised if there is no ForeignKey from model to
parent_model.

And now for the exception:

if fk_name:
        fks_to_parent = [f for f in opts.fields if f.name == fk_name]
        if len(fks_to_parent) == 1:
            fk = fks_to_parent[0]
            if not isinstance(fk, ForeignKey) or \
                    (fk.rel.to != parent_model and
                     fk.rel.to not in parent_model._meta.get_parent_list()):
                raise Exception("fk_name '%s' is not a ForeignKey to %s" % (fk_name, parent_model))

I am guessing that you have not setup the fields or relationships in your model correctly.

I did read somewhere about someone solving this problem by adding pass to their class like so:

class Whatever(x.x):
        pass
Ailurophile answered 27/6, 2012 at 4:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.