使用反向字段时自动创建一对一关系

时间:2017-03-30 13:37:55

标签: django django-models foreign-keys relational-database one-to-one

创建模型的两个实例并使用OneToOneField连接它们时,会创建连接并在创建对象时自动保存:

from django.db import models

class MyModel(models.Model):
    name = models.CharField(primary_key=True, max_length=255)
    next = models.OneToOneField('self', on_delete=models.SET_NULL, related_name='prev', null=True, blank=True)

>>> m2 = MyModel.objects.create(name="2")
>>> m1 = MyModel.objects.create(name="1", next=m2)
>>> m2.prev
<MyModel: 1>
>>> m2.refresh_from_db()
>>> m2.prev
<MyModel: 2>

但是,在创建相同连接但使用反向字段时,创建也会自动完成,但保存。

>>> m1 = MyModel.objects.create(name="1")
>>> m2 = MyModel.objects.create(name="2", prev=m1)
>>> m1.next
<MyModel: 2>
>>> m1.refresh_from_db()
>>> m1.next

请注意,最后一个语句不会打印任何内容,因为它返回None

如何在使用反向字段创建时始终保存关系,而不必每次都手动使用.save()

2 个答案:

答案 0 :(得分:5)

实现这一目标的简单方法可能是使用pre_save / post_save信号,您认为这是一个可行的解决方案。但不确定这个答案有多可行,尝试制作一些mod并看看是否有效!

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

class MyModel(models.Model):
    name = models.CharField(primary_key=True, max_length=255)
    next = models.OneToOneField('self', on_delete=models.SET_NULL, related_name='prev', null=True, blank=True)

@receiver(post_save, sender=MyModel)
def mymodel_post_save(sender, instance, **kwargs):
     if hasattr(instance, 'prev'): # if prev exists
          # now check if prev is added next
          if not instance.prev.next: # if next is not present 
                 instance.prev.next = instance
                 MyModel.objects.filter(
                     pk=instance.prev.pk
                 ).update(next=instance)

答案 1 :(得分:3)

这是一个糟糕的主意。

你会混淆每个看你代码的人。

我甚至对MyModel.objects.create的工作感到惊讶,而不是抛出无效的关键字参数。我可能会打开这个。

致电时:

m2 = MyModel.objects.create(name="2", prev=m1)

为了按预期工作,.create方法需要在m1实例上调用save,因为m1是保存关系的实例(通过next })

谁会期待?你正在隐藏功能。来自PEP 20

的一些引用
  

明确比隐含更好。

     

应该有一个 - 最好只有一个 - 显而易见的方法。

我的建议是将关系更改为:

class MyModel(models.Model):
    name = models.CharField(primary_key=True, max_length=255)
    prev = models.OneToOneField('self',
                                null=True,
                                blank=True,
                                related_name='next',
                                on_delete=models.SET_NULL)

因为,它更有可能在创建时知道prev是什么。