示例化Django模型引发TypeError:isinstance()arg 2必须是类、类型或类和类型的元组

juzqafwq  于 2023-03-13  发布在  Go
关注(0)|答案(6)|浏览(244)

我有一个运行正常的Django应用程序,在过去的几个月里一直运行在DEBUG模式下,当我将网站更改为生产模式时,当我点击一个试图创建新的引用模型对象的特定视图时,我开始收到以下发送给我的异常邮件。

Traceback (most recent call last):

 File "/usr/local/lib/python2.7/dist-packages/Django-1.4.2-py2.7.egg/django/core/handlers/base.py", line 111, in get_response
   response = callback(request, *callback_args, **callback_kwargs)

 File "/usr/local/lib/python2.7/dist-packages/Django-1.4.2-py2.7.egg/django/contrib/auth/decorators.py", line 20, in _wrapped_view
   return view_func(request, *args, **kwargs)

 File "/var/django/acclaimd2/program/api.py", line 807, in put_interview_request
   referral = Referral()

 File "/usr/local/lib/python2.7/dist-packages/Django-1.4.2-py2.7.egg/django/db/models/base.py", line 349, in __init__
   val = field.get_default()

 File "/usr/local/lib/python2.7/dist-packages/Django-1.4.2-py2.7.egg/django/db/models/fields/related.py", line 955, in get_default
   if isinstance(field_default, self.rel.to):

TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types

正如你所看到的,仅仅是试图示例化一个引用模型对象就会触发这个异常。

class Referral (models.Model):
    opening = models.ForeignKey(Opening,related_name='referrals',null=False,blank=False)
    origin_request = models.ForeignKey('common.request',related_name='referrals',null=True,default=None)
    candidate = models.ForeignKey(User,related_name='referrals',null=False,blank=False)
    intro = models.TextField(max_length=1000,null=False,blank=False)
    experience = models.TextField(max_length=5000,null=False,blank=False)
    email = models.CharField(max_length=255,null=False,blank=False)
    phone = models.CharField(max_length=255,null=False,blank=True,default='')

    def __unicode__(self):
        return u"%s" % self.id

这是Django中的一个bug还是我在不知不觉中做了一些我不应该做的事情?有人对修复或变通有什么建议吗?

mcdcgff0

mcdcgff01#

更新(解决方案如下)

我一直在深入研究Django模型代码,似乎有一个bug,当在ForeignKey中使用基于“app.model”的标识符时,会产生一个竞态条件。当应用程序在生产模式下运行而不是在DEBUG下运行时,在上面的异常中引用的ForeignKey.get_default方法会尝试检查提供的默认值是否是相关字段的示例(self.rel.to):

def get_default(self):
    "Here we check if the default value is an object and return the to_field if so."
    field_default = super(ForeignKey, self).get_default()
    if isinstance(field_default, self.rel.to):
        return getattr(field_default, self.rel.get_related_field().attname)
    return field_default

最初,当用基于字符串的相关字段示例化外键时,self.rel.to被设置为基于字符串的标识符。在www.example.com中有一个单独的函数related.py称为add_lazy_relation,在正常情况下,该函数试图将该基于字符串的标识符转换为模型类引用。因为模型是以惰性方式加载的,这个转换可以推迟到AppCache完全加载之后。
因此,如果在AppCache完全填充之前,在基于字符串的ForeignKey关系上调用get_default,则可能会引发TypeError异常。显然,将应用程序置于生产模式足以改变模型缓存的时间,从而导致此错误开始发生。

解决方案

看起来这确实是Django中的一个bug,但是如果你遇到这个问题,这里有一个解决方法。在示例化一个有问题的模型之前,添加下面的代码片段:

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

这将检查AppCache上的已加载标志,以确定该高速缓存是否已完全填充。如果未完全填充,我们将强制缓存立即完全填充。这样问题就解决了。

s4n0splo

s4n0splo2#

在我的例子中,我有一个模型'Entity'和一个继承了'AbstractBaseUser'的'User'。一个'User'模型有一个实体字段,其中实体的ForeignKey是这样配置的:

entity = m.ForeignKey('core.Entity', on_delete=m.SET_NULL, null=True)

最后,变通方案是将其更改为

from core.models import Entity
entity = m.ForeignKey(Entity, on_delete=m.SET_NULL, null=True)

我不知道问题的原因,但它就是那样工作的

x33g5p2x

x33g5p2x3#

另一个原因:
确保所有的外键都在同一个应用程序(应用程序标签)中的同一个引用模型中。我在这个问题上有一段时间把头撞在墙上。

class Meta:
    db_table = 'reservation'
    app_label = 'admin'
vqlkdk9b

vqlkdk9b4#

正如在其他答案中已经强调的那样,最常见的原因似乎是引用了使用'app.Model'语法的模型。
然而,要弄清楚到底是哪个型号造成了麻烦,可能仍然是一个相当大的挑战。
我发现这个问题的一个快速解决方案是直接转到引发异常的位置(django.db.models.fields.related.ForeignKey.get_default),然后添加以下print状态

def get_default(self):
        """Return the to_field if the default value is an object."""
        field_default = super().get_default()
        # Add this line:
        print(f'Here is our small little bug: {field_default}, {self.remote_field.model}')
        if isinstance(field_default, self.remote_field.model):
            return getattr(field_default, self.target_field.attname)
        return field_default

现在,有问题的模型的名称已经打印出来,在代码中找到相关位置不再是一个大问题。
在Django 2.2上测试。

eimct9ow

eimct9ow5#

在我的例子中,我在类调用中犯了一个错误,用引号把类名括起来。删除引号为我解决了这个错误。请看下面的错误代码:

from django.db import models
from django.contrib.auth.models import AbstractUser
from account.models import Profile

class User(AbstractUser):
    first_name = None;
    last_name = None;
    profile = models.ForeignKey(
        'Profile',
        on_delete=models.CASCADE,
    );
xfyts7mz

xfyts7mz6#

在大多数情况下,此错误是由于您在具有模型关系(外键等)的另一个模型中引用已删除/注解的模型而导致的。
1.如果您已删除或注解掉某些模型
1.检查所有的模型是否有外键、一对一等指向已删除的模型
1.注意你在哪里引用模型(下面的例子),因为python不会引发任何明显的错误

center = models.ForeignKey("accounts.Center", blank=True, null=True,
                              on_delete=models.SET_NULL, related_name="staffs")

相关问题