django 在父子模型类之间保存实例

Posted

技术标签:

【中文标题】django 在父子模型类之间保存实例【英文标题】:django save instance between parent and child model class 【发布时间】:2013-05-21 18:57:24 【问题描述】:

我在表单保存时遇到了这个问题,需要将数据保存在某个地方,然后通过付款流程,然后成功检索数据并保存到正确的模型。

我已经看到使用会话完成了这项工作,但是在 commit=False 时使用一些 hacky 方式来持久化文件上传,而且它看起来不是很 Python

我在想如果我有一个模型类A,并且有一个扩展A的子类,比如A_Temp

class A(models.Model):
  name = models.CharField(max_lenght=25)
  image = models.ImageField()

class A_Temp(A):
  pass

class AForm(forms.ModelForm):
  class Meta:
    model = A_Temp

模型表单(A_Temp)保存时,存储到A_Temp,支付成功后,将实例移动到父模型类A中。

以下是问题:

    以前有人做过吗?

    如何将子模型类的实例正确移动到父模型类?

编辑:

还有其他不同的方法可以做到这一点,例如向表中添加额外的字段,是的,如果我在没有 ORM 框架的情况下使用 php,我会这样做,但由于 ORM 在 django 中相当不错,我想我可能会尝试不同的东西。

既然我在这里问,意味着我也不相信这种方法。你有什么想法?

【问题讨论】:

我假设付款过程需要在正常的 django 请求/响应周期之外进行? 向A类添加一个额外的字段并更新它而不是在A和A_Temp之间来回移动是否可行? 我一开始也是这么想的。 我尝试添加字段的替代方法的原因是,当我们有额外的字段来指示记录时,表可能包含无用的记录,在大数据规模下,需要创建索引来维护性能和清理。另一方面,这种情况下的临时模型作为沙箱执行 【参考方案1】:

正如问题 cmets 中所建议的,向您的模型中添加一个包含付款状态的额外字段可能是最简单的方法。从概念上讲,它是同一个对象,只是付款后状态会发生变化。正如您所指出的,您将需要逻辑来从数据库中清除永远不会通过所需状态(例如付款)的项目。这可能涉及将payment_statestate_change_time 字段添加到您的模型中,以指示状态上次更改的时间。如果状态为PAYMENT_PENDING 的时间过长,则可能会清除该记录。

如果您按照建议将未付款的项目存储在不同的表中,您仍然需要管理该表以确定何时可以安全地删除项目。例如,如果从未处理过付款,您何时会从A_temp 表中删除记录?此外,拥有一个单独的表意味着您实际上只有两种可能的状态,已付款和未付款,由记录所在的表确定。拥有一个带有payment_state 的表可能更灵活,因为它允许您根据需要扩展状态。例如。假设您决定需要付款状态ITEM_SUBMITTEDAWAITING_PAYMENTPAYMENT_ACCEPTEDPAYMENT_REJECTED。这都可以用一个state 字段来实现。如果按照您的描述实现,您需要为每个州创建一个单独的表。

说了这么多,如果您仍然想拥有一个单独的表结构,您可以创建一个函数,将值从A_temp 的实例复制到A。像下面这样的东西可能会起作用,但任何关系类型字段,例如 ForeignKey 都可能需要特别注意。

def copy_A_temp_to_A(a, a_temp):
    for field_name in a._meta.fields:
        value = getattr(a, field_name)
        setattr(a_temp, field_name, value)

当您需要从A_temp 移动到A 时,您必须实例化一个A 实例,然后调用复制函数save 实例并从其中删除A_temp 实例数据库。

【讨论】:

感谢您的冗长回答,是的,我知道我还需要清除临时表。这个想法可能看起来很愚蠢,我在这里问的原因是因为我希望 A 表尽可能干净,对于不同的状态,也可以在 A_Temp 模型中添加额外的字段。我想创建分割列表的数据库视图可能是在同一模型中拥有额外字段的另一种选择。 数据库视图可能是解决“干净”问题的一种方法,但这也有其自身的问题。例如,Django 没有对数据库视图的原生支持,因此无法在单元测试期间创建视图。并不是所有的数据库后端都支持数据库视图,例如 sqlite。尽管如此,还是可以做到这一点(我也使用过这种方法)。您的数据库视图模型可以设置class Meta: managed=False db_table=<name_of_main_table> 实现一个只返回付费项目的自定义管理器也可能很有用。例如,代替A.objects.all(),使用A.paid_objects.all()。或者,覆盖 objects 默认管理器以仅返回付费实例。 这是一个冗长的方法

以上是关于django 在父子模型类之间保存实例的主要内容,如果未能解决你的问题,请参考以下文章

如何运行保存在模型实例中的 Django 代码?

如何在保存实例之前获取 django 模型实例的 pk

风格 - 何时序列化 Django 模型实例:信号与模型的保存方法

一起保存父子模型对象

如何通过 HTML 中的 JavaScript 在 Django 中保存模型实例

在 Django Admin 中使用 DateTimeField 保存模型实例会丢失微秒分辨率