Django的post_save信号对使用多表继承的模型表现得很奇怪

hek*_*ran 4 django django-models

Django的post_save信号对使用多表继承的模型表现得很奇怪

我注意到Django的post_save信号在使用具有多表继承的模型时的奇怪行为.

我有这两个模型:

class Animal(models.Model):
    category = models.CharField(max_length=20)

class Dog(Animal):
    color = models.CharField(max_length=10)
Run Code Online (Sandbox Code Playgroud)

我有一个名为echo_category的帖子保存回调:

def echo_category(sender, **kwargs):
    print "category: '%s'" % kwargs['instance'].category
post_save.connect(echo_category, sender=Dog)
Run Code Online (Sandbox Code Playgroud)

我有这个装置:

[
    {
        "pk": 1,
        "model": "animal.animal",
        "fields": {
            "category": "omnivore"
        }
    },

    {
        "pk": 1,
        "model": "animal.dog",
        "fields": {
            "color": "brown"
        }
    }
]
Run Code Online (Sandbox Code Playgroud)

在程序的每个部分中,除了post_save回调之外,以下情况都是如此:

from animal.models import Dog
Dog.objects.get(pk=1).category == u'omnivore' # True
Run Code Online (Sandbox Code Playgroud)

当我运行syncdb并安装fixture时,运行echo_category函数.syncdb的输出是:

$ python manage.py syncdb --noinput
Installing json fixture 'initial_data' from '~/my_proj/animal/fixtures'.
category: ''
Installed 2 object(s) from 1 fixture(s)
Run Code Online (Sandbox Code Playgroud)

这里奇怪的是dog对象的category属性是一个空字符串.为什么它不像其他地方那样"杂食"?

作为临时(希望)解决方法,我在post_save回调中从数据库重新加载对象:

def echo_category(sender, **kwargs):
    instance = kwargs['instance']
    instance = sender.objects.get(pk=instance.pk)
    print "category: '%s'" % instance.category
post_save.connect(echo_category, sender=Dog)
Run Code Online (Sandbox Code Playgroud)

这有效,但它不是我喜欢的东西,因为当模型从另一个模型继承并且必须再次访问数据库时,我必须记住这样做.另一个奇怪的事情是我必须使用instance.pk来获取主键.普通的'id'属性不起作用(我不能使用instance.id).我不知道为什么会这样.也许这与category属性不正确的原因有关?

Clé*_*ent 5

这是因为使用loaddata/ syncdbcommand 从fixture加载的数据在数据库中保存为raw:只保存生成的模型表的字段,以避免为类层次结构中的所有模型访问数据库.

但是,当模型保存为原始模型时,您会raw在信号中获得额外的关键字参数,因此您可以正确处理案例.你的信号会像那样结束:

def echo_category(sender, **kwargs):
    if kwargs.get('raw', False):
        instance = sender.objects.get(pk=kwargs['instance'].pk)
    else:
        instance = kwargs['instance']
    print "category: '%s'" % instance.category

post_save.connect(echo_category, sender=Dog)
Run Code Online (Sandbox Code Playgroud)

这样,在处理灯具时,您将只获得额外的数据库查询(我想这在您的情况下是可接受的).

关于你的另一个问题:

另一个奇怪的事情是我必须使用instance.pk来获取主键.普通的'id'属性不起作用(我不能使用instance.id).我不知道为什么会这样.

id并且pk语义略有不同.在实施例中,id一个的Dog对象的AutoField定义的(自动的)在你的Animal类.该pk然而,是OneToOneField(再次,自动地限定在)Dog类.

实际上,两个字段始终具有相同的值.但是,就像id来自的字段一样Animal,Dog对象保存为raw也不存在.

希望有所帮助.

编辑:这个问题已经在django的trac上报道.