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

vag*_*ond 44 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)
Run Code Online (Sandbox Code Playgroud)

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

编辑说这些模型在不同的应用程序中.

Joh*_*ett 42

看看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)
Run Code Online (Sandbox Code Playgroud)

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


Dmi*_*try 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')
Run Code Online (Sandbox Code Playgroud)

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

  • +1为此.问题的关键是模型属于不同的应用程序.这与信号的用例相匹配:"允许解耦的应用程序在框架中的其他位置发生操作时得到通知".其他提议的解决方案有效,但引入了不必要的A-> B依赖,基本上捆绑了两个应用程序.信号允许A保持与B分离. (2认同)
  • 来自文档的@MariusGedminas:`请注意,Django默认将信号处理程序存储为弱引用,因此如果您的处理程序是本地函数,则可能是垃圾回收.为了防止这种情况,当你调用信号的connect()时传递weak = False (2认同)

Jar*_*die 14

最直接的方法是覆盖 ModelA 的save方法:

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)
Run Code Online (Sandbox Code Playgroud)

  • 这样做的麻烦在于,如果您在管理中有一个内联表单并使用它同时创建一个 ModelB 实例,那么不幸的是它会中断 - 它会尝试创建两个 ModelB 并且可怕地死掉。 (3认同)
  • 可能希望通过不将args命名为super来更加面向未来.我会建议编辑. (3认同)

rea*_*iek 8

我知道这有点晚了,但我想出了一个更干净、更优雅的解决方案。考虑这个代码:

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)
Run Code Online (Sandbox Code Playgroud)

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

  • 就我而言,这完成了 2 个 ModelA 记录的创建...我不知道为什么... Django 创建模型的方式非常奇怪。 (2认同)

jtl*_*lai 7

我收集了一些不同的答案(因为没有一个是直接为我工作的)并想出了这个。觉得它很干净,所以我分享了它。

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)
Run Code Online (Sandbox Code Playgroud)

它按照@Dmitry 的建议使用 Signal。正如@daniel-roseman 在@jarret-hardie 的回答中所评论的那样,Django Admin 有时会尝试为您创建相关对象(如果您更改内联表单中的默认值),我遇到了这种情况,因此进行了 hasattr 检查。漂亮的装饰器技巧来自@shadfc在模型创建的 Create OneToOne 实例中的回答


sch*_*eck 0

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