gpt4 book ai didi

python - 无法使用 pre_save 信号在 Django 模型中保存相关对象

转载 作者:行者123 更新时间:2023-11-28 19:02:59 25 4
gpt4 key购买 nike

我必须执行 multi-aspect type of inheritance from UML在 Django ORM 中。我有 Contract 数据类型,根据客户类型(普通客户或商业客户)可以将其分类为 RegularContractBusinessContract。此外,契约(Contract)可以有到期日或不可到期(未指定有效期多长时间),因此它也可以是 ExpiringContractNonExpiringContract 类型。这是概念图的样子: enter image description here

这就是我的实现方式:enter image description here

models.py代码:

class Contract(models.Model):
approval_date = models.DateTimeField(null=False)

def __getattr__(self, item):
if self.expiringcontract:
return getattr(self.expiringcontract, item)
elif self.nonexpiringcontract:
return getattr(self.nonexpiringcontract, item)


class ContractExpirationExtension(models.Model):
base = models.OneToOneField("website.Contract",
on_delete=models.CASCADE)

class Meta:
abstract = True


class ExpiringContract(ContractExpirationExtension):
termination_date = models.DateTimeField()

@property
def duration(self):
return self.termination_date - self.base.approval_date


class NonExpiringContract(ContractExpirationExtension):

@property
def duration(self):
return timedelta(days=100)


class ContractTypeExtension(models.Model):

base = models.OneToOneField("website.Contract", on_delete=models.CASCADE)
termination_delay = models.PositiveSmallIntegerField(default=30)

class Meta:
abstract = True

@classmethod
def create(cls, approval_date, contract_expiration_type, termination_delay, **kwargs):
type_extension = cls(termination_delay=termination_delay)
base = Contract(approval_date=approval_date)
expiration_type = contract_expiration_type(**kwargs)
expiration_type.base = base
type_extension.base = base
if contract_expiration_type.__name__ == ExpiringContract.__name__:
type_extension.base.expiringcontract = expiration_type
elif contract_expiration_type.__name__ == NonExpiringContract.__name__:
type_extension.base.nonexpiringcontract = expiration_type
return type_extension

def __getattr__(self, item):
if self.base:
return getattr(self.base,item)


class RegularContract(ContractTypeExtension):
termination_delay = models.PositiveSmallIntegerField(validators=[validate_term_delay_regular], blank=False)


class BusinessContract(ContractTypeExtension):
termination_delay = models.PositiveSmallIntegerField(validators=[validate_term_delay_business], blank=False)

当我们需要创建新的契约(Contract)模型实例时,我们使用继承ContractTypeExtension 抽象类的类的create() 方法。在 create() 方法中,我创建了 Contract 基础实例,并根据我传递给 create() 方法:

@classmethod
def create(cls, approval_date, contract_expiration_type, termination_delay, **kwargs):
type_extension = cls(termination_delay=termination_delay)
base = Contract(approval_date=approval_date)
expiration_type = contract_expiration_type(**kwargs)
expiration_type.base = base
type_extension.base = base
if contract_expiration_type.__name__ == ExpiringContract.__name__:
type_extension.base.expiringcontract = expiration_type
elif contract_expiration_type.__name__ == NonExpiringContract.__name__:
type_extension.base.nonexpiringcontract = expiration_type
return type_extension

因为我的常规或业务契约(Contract)实例中包含其他模型实例,所以我无法在不先保存 baseexpiration_type 实例的情况下保存它,所以我决定创建pre_save 信号将执行此操作:

信号.py:

from django.db.models.signals import pre_save, pre_delete from django.dispatch import receiver

from .models import RegularContract, BusinessContract

@receiver(pre_save, sender=RegularContract)
@receiver(pre_save, sender=BusinessContract)
def pre_save_contract(sender, instance, *args,**kwargs):
print("Pre_save")
if not instance.id:
instance.base.save()
try:
instance.base.expiringcontract.save()
except (TypeError, ValueError):
instance.base.nonexpiringcontract.save()

我在应用程序的 __init__apps.py 配置中注册了我的信号文件:

apps.py:

from django.apps import AppConfig


class WebsiteConfig(AppConfig):
name = 'website'

def ready(self):
import website.signals

网站.__init__.py:

default_app_config = 'website.apps.WebsiteConfig'

为了测试我的代码,我编写了简单的测试用例:

class BusinessContractTestCase(TestCase):

def setUp(self):
pass

def test_exprirating_creation(self):
approval_date = datetime.today()
termination_delay = 30
termination_date = approval_date+timedelta(days=720)
contract = BusinessContract.create(approval_date=approval_date, contract_expiration_type=ExpiringContract,
termination_delay=termination_delay,
termination_date=termination_date)
contract.save()
self.assertEqual(contract.termination_date.date(), ExpiringContract.objects.first().termination_date.date())


class RegularContractTestCase(TestCase):

def test_exprirating_creation(self):
approval_date = datetime.today()
termination_delay = 30
termination_date = approval_date + timedelta(days=720)
contract = RegularContract.create(approval_date=approval_date,
contract_expiration_type=ExpiringContract,
termination_delay=termination_delay,
termination_date=termination_date)
contract.save()
self.assertEqual(contract.termination_date.date(),
ExpiringContract.objects.first().termination_date.date())

但是当尝试运行这个测试时,它们失败了,我得到了这个错误:

Error
Traceback (most recent call last):
File "/home/ubuntu/workspace/webapp/website/tests.py", line 21, in test_exprirating_creation
contract.save()
File "/home/ubuntu/workspace/venv/lib/python3.5/site-packages/django/db/models/base.py", line 685, in save
"unsaved related object '%s'." % field.name
ValueError: save() prohibited to prevent data loss due to unsaved related object 'base'.

那么为什么在我的代码中没有触发pre_save 信号呢?

最佳答案

经过短暂的调试,我明白了我的问题(感谢 Willem Van Onsempre_save 指出了这个细节。)这就是我解决问题的方法。我稍微修改了 create() 方法。我没有将 base 直接分配给新创建的实例,也没有将 expiration_type 分配给 base 我将它们保存到临时变量中,稍后我可以在我的信号方法中使用它们:

@classmethod
def create(cls, approval_date, contract_expiration_type, termination_delay, **kwargs):
type_extension = cls(termination_delay=termination_delay)
base = Contract(approval_date=approval_date)
expiration_type = contract_expiration_type(**kwargs)
type_extension.temp_base = base
if contract_expiration_type.__name__ == ExpiringContract.__name__:
type_extension.temp_expiringcontract = expiration_type
elif contract_expiration_type.__name__ == NonExpiringContract.__name__:
type_extension.base.temp_nonexpiringcontract = expiration_type
return type_extension

然后在 pre_save 信号的 signals.py 中,我分别从临时变量中保存基数并分配给实例的基数,并分别从临时变量中分配我的到期/未到期契约(Contract)类型实例建立并保存它:

@receiver(pre_save, sender=RegularContract)
@receiver(pre_save, sender=BusinessContract)
def pre_save_contract(sender, instance, *args, **kwargs):
print("Pre_save")
instance.temp_base.save()
instance.base = instance.temp_base
if hasattr(instance,"temp_expiringcontract"):
instance.base.expiringcontract = instance.temp_expiringcontract
instance.base.expiringcontract.save()
else:
instance.base.nonexpiringcontract = instance.temp_nonexpiringcontract
instance.base.nonexpiringcontract.save()

这可能不是最好的解决方案,但至少它有效。

关于python - 无法使用 pre_save 信号在 Django 模型中保存相关对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50475980/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com