Django可以自动创建一个相关的一对一模型吗?

时间:2009-10-30 21:57:48

标签: django django-models models

我在不同的应用程序中有两个模型:modelA和modelB。他们有一对一的关系。有没有办法在保存modelA时django可以自动创建和保存ModelB?

class ModelA(models.Model):
    name = models.CharField(max_length=30)

class ModelB(models.Model):
    thing = models.OneToOneField(ModelA, primary_key=True)
    num_widgets = IntegerField(default=0)

当我保存新的ModelA时,我想要一个条目,以便在ModelB中自动保存。我怎样才能做到这一点?有没有办法在ModelA中指定它?或者这是不可能的,我只需要在视图中创建和保存ModelB?

编辑说模特在不同的应用程序中。

9 个答案:

答案 0 :(得分:40)

django-annoying中查看AutoOneToOneField。来自文档:

from annoying.fields import AutoOneToOneField

class MyProfile(models.Model):
    user = AutoOneToOneField(User, primary_key=True)
    home_page = models.URLField(max_length=255)
    icq = models.CharField(max_length=255)

(django-annoying是一个很棒的小库,包括像render_to装饰器和get_object_or_None和get_config函数这样的宝石)

答案 1 :(得分:28)

与m000指出的一样,您的模型存在于不同的应用程序中。通常您使用未编写的应用程序,因此为了允许更新,您需要一种分离的方式来创建逻辑相关的模型。在我看来,这是首选的解决方案,我们在一个非常大的项目中使用它。

使用信号:

在models.py中:

from django.db.models import signals


def create_model_b(sender, instance, created, **kwargs):
    """Create ModelB for every new ModelA."""
    if created:
        ModelB.objects.create(thing=instance)

signals.post_save.connect(create_model_b, sender=ModelA, weak=False,
                          dispatch_uid='models.create_model_b')

如果两个应用都是现成的,您可以创建一个单独的应用来保存此models.py文件。

答案 2 :(得分:14)

最简单的方法是使用ModelA的override the save method

class ModelA(models.Model):
    name = models.CharField(max_length=30)

    def save(self, force_insert=False, force_update=False):
        is_new = self.id is None
        super(ModelA, self).save(force_insert, force_update)
        if is_new:
            ModelB.objects.create(thing=self)

答案 3 :(得分:3)

我知道它有点晚了,但我提出了一个更清洁,更优雅的解决方案。 请考虑以下代码:

class ModelA(models.Model):
    name = models.CharField(max_length=30)

    @classmethod
    def get_new(cls):
        return cls.objects.create().id



class ModelB(models.Model):
    thing = models.OneToOneField(ModelA, primary_key=True, default=ModelA.get_new)
    num_widgets = IntegerField(default=0)

当然你也可以使用lambda,只要你返回相关对象的整数id:)

答案 4 :(得分:1)

我收集了几个不同的答案(因为它们都没有为我开箱即用)并想出了这个答案。认为它非常干净,所以我分享它。

from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(post_save, sender=ModelA)
def create_modelb(sender, instance, created, **kwargs):
    if created:
        if not hasattr(instance, 'modelb'):
            ModelB.objects.create(thing=instance)

它使用Signal作为@Dmitry建议。正如@ daniel-roseman在@ jarret-hardie的回答中评论的那样,Django Admin有时会尝试为你创建相关对象(如果你改变了内联形式的默认值),我遇到了这个问题,因此hasattr检查。漂亮的装饰者提示来自@ shadfc在Create OneToOne instance on model creation

中的回答

答案 5 :(得分:0)

您可以使用保存记录后触发的post_save-hook。有关django信号的更多文档,请参阅here。在this page上,您可以找到有关如何在模型上应用挂钩的示例。

答案 6 :(得分:0)

我认为你想使用django's model inheritance。如果以下陈述为真,则此选项非常有用:ModelA是ModelB(例如,餐厅是位置)。

您可以定义:

class ModelB(models.Model):
    field1 = models.CharField(...)

class ModelA(ModelB): field2 = models.CharField(...)

现在,您可以创建ModelA的实例并设置field2 field1。如果保存此模型,它还将创建ModelB的实例,该实例获取分配的field1的值。这一切都在幕后透明地完成。

虽然您可以执行以下操作:

a1 = ModelA()
a1.field1 = "foo"
a1.field2 = "bar"
a1.save()
a2 = ModelA.objects.get(id=a1.id)
a2.field1 == "foo" # is True
a2.field2 == "bar" # is True
b1 = ModelB.objects.get(id=a1.id)
b1.field1 == "foo" # is True
# b1.field2 is not defined

答案 7 :(得分:0)

只需创建一个创建并返回空ModelA的函数,并将“thing”的默认命名参数设置为该函数。

答案 8 :(得分:0)

如果您在管理面板中使用InlineForm,则可以这样做。

但是当然在其他情况下也需要检查(例如在DRF或手动创建模型实例中)

from django.contrib import admin
from django.forms.models import BaseInlineFormSet, ModelForm

class AddIfAddParentModelForm(ModelForm):
    def has_changed(self):
        has_changed = super().has_changed()

        if not self.instance.id:
            has_changed = True

        return has_changed

class CheckerInline(admin.StackedInline):
    """ Base class for checker inlines """
    extra = 0
    form = AddIfAddParentModelForm