【问题标题】:dependent multi object validation in django admindjango admin中的依赖多对象验证
【发布时间】:2016-04-08 14:53:13
【问题描述】:

TimeClass 的每个“时间范围”条目相互依赖。

它们不能重叠,并且 start_time

models.py

class Xyz(models.Model):
    ...   

class TimeRangeClass(models.Model)
    start_time = models.TimeField()
    end_time = models.TimeField()
    xyz = models.ForeignKey(Xyz)
    # other fields here

    def clean(self):
        # Here I loop through TimeRangeClass.objects.all() and 
        # check for conflicts through my custom "my_validator_method".
        # If there is a conflict I throw an error
        #(I've since modified it to just be one single query as per Titusz advice)             
        for each in TimeRangeClass.objects.filter(xyz=self.xyz).exclude(id=self.id):
            my_validator_method(start_time1=self.start_time, 
                                end_time1=self.end_time, 
                                start_time2=each.start_time, 
                                end_time2=each.end_time)

admin.py

from .models import TimeRangeClass, Xyz
class TimeRangeClassInLine(admin.TabularInline):
    model = TimeRangeClass
    extra = 3

@admin.register(Xyz)
class Xyz(admin.ModelAdmin):
    exclude = []
    inlines = [TimeRangeClassInLine]

问题:我可以通过管理员一次编辑/添加多个 TimeRangeClass。但鉴于 models.Model clean 方法一次只评估 1 个更改,我无法验证多个编辑相互之间的关系。

例子:

  1. 保存条目 1 和条目 2 而不发生冲突

  2. 更改 Entry2 以产生验证错误

  3. 调整 Entry1(而不是 #2),使其不重叠

  4. 这不会注册,因为这两个更改都不会写入数据库。

我正在寻找解决方法。

【问题讨论】:

  • 你应该对问题给出更具体的解释。您在哪里/如何更改条目 #2?从管理界面、视图还是表单?

标签: python django django-models django-admin django-validation


【解决方案1】:

关于问题的一些提示:

在检查重叠行时,您不应遍历整个表。只需过滤有问题的行...类似于:

overlaps = TimeRangeClass.objects.filter(
    Q(start_time__gte=self.start_time, start_time__lt=self.end_time) | 
    Q(end_time__gt=self.start_time, end_time__lte=self.end_time)
)

overlaps 现在是一个查询集,当您对其进行迭代时会进行评估,并且只返回冲突的对象。

如果您使用 Django 和 postgres,您应该查看 https://docs.djangoproject.com/es/1.9/ref/contrib/postgres/fields/#datetimerangefield

一旦您有了冲突的对象,您应该能够在函数中更改它们的开始和结束时间并保存更改。 Model.save() 不会自动调用 model.clean() 方法。但请注意,如果您从 Django 管理员保存对象,它在保存之前调用 model.clean() 方法。

类似这样的:

def clean():
    overlaps = TimeRangeClass.overlaps.for_trc(self)
    for trc_object in overlaps:
        fixed_object = fix_start_end(trc_object, self)
        fixed_object.save()

如果您有勇气,您还应该阅读事务,以使数据库中多个对象的突变全部成功或全部失败,并且两者之间没有任何关系。

def clean():
    with transaction.atomic():
        # do your multi object magic here ...

已澄清问题的更新:

如果您想验证或预处理/处理来自管理内联的数据,您必须挂钩到相应的 ModelAdmin 方法。有多种方法可以解决这个问题。我想最简单的方法是覆盖ModelAdmin.save_fromset。在这里,您可以在保存之前访问所有内联表单。

【讨论】:

  • 感谢您建议使用 Q() 而不是遍历查询集。我不确定为什么我决定在这种情况下循环,因为在其他情况下我确实使用了类似的查询。
  • 我简要查看了 transaction.atomic() 的文档。这是否允许我为 clean 方法比较尚未保存的变量?
  • 尚未保存的变量是什么意思?您的尚未保存的变量来自哪里?您正在将新的最终未保存的 TimeRangeClass 实例与您通过查询集加载到内存中的现有实例进行比较。
  • 我在后台使用 TimeRangeClass 作为另一个模型的内联。因此,我可以一次修改/添加多个 TimeRangeClass。但是我只能通过 clean 方法一次评估一个 TimeRangeClass。
  • 谢谢,ModelAdmin.save_fromset 似乎正是我想要的。
猜你喜欢
  • 2012-11-11
  • 2017-05-18
  • 2015-04-02
  • 1970-01-01
  • 1970-01-01
  • 2011-12-28
  • 1970-01-01
  • 1970-01-01
  • 2011-06-12
相关资源
最近更新 更多