我在不同的应用程序中有两个模型: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?
编辑说模特在不同的应用程序中。
答案 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