2016-11-21 58 views
0

我有一个Django模型,有一个开始和结束日期。 结束日期可以为空,如果模型处于活动状态,则将其视为正在进行的 。在Django的自定义验证管理员休息日期时间部件

模型的每个实例都有一个由动态列表生成的字段foo。 Foo在实例中并不唯一,但具有相同foo字段的两个实例不能同时处于活动状态。

我正在使用的数据库是PostgreSQL,并且afaik无法在数据库级别实施。然后我选择在管理员层面执行此操作。当添加我的模型的新实例时,我想验证此时没有冲突的实例并引发验证错误。当我要在本地测试时,日期时间小部件完全不显示,只有文本框。

我的问题是这是最好的方式来实现我想要的东西,如果是的话为什么是datetime部件丢失,我该如何取回它?

代码片段:

class MyModelAdmin(admin.ModelAdmin): 
    list_display = ['name', 
        'foo', 
        'active', 
        'created', 
        'started', 
        'ended'] 

    list_editable = ['name', 
        'active', 
        'ended'] 

    def formfield_for_dbfield(self, db_field, *args, **kwargs): 
     if db_field.name.lower() == 'foo': 

      choices = sorted(some_dynamic_list) 

      db_field.choices = choices 

     return super().formfield_for_dbfield(db_field, *args, **kwargs) 

    def get_changelist_form(self, request, *args, **kwargs): 
     parent_form = super().get_changelist_form(request, *args, **kwargs) 
     return self.get_childform(parent_form) 

    def get_form(self, request, obj=None, *args, **kwargs): 
     parent_form = super().get_form(request, obj, *args, **kwargs) 
     return self.get_childform(parent_form, obj) 

    def get_childform(self, parent_form, obj=None): 
     class ChildForm(parent_form): 

      def clean(self, *args, **kwargs): 
       instance_dict = {} 
       for instance in MyModel.objects.filter(active=True): 
        foo = instance.config_type.lower() 
        if foo in instance_dict: 
         instance_dict[foo].append(instance) 
        else: 
         instance_dict[foo] = [instance, ] 
       if not obj: 
        for foo, instances in instance_dict.items(): 
         for list_index, instance in enumerate(instances): 
          for other_instance in instances[list_index + 1:]: 
           if self.instances_conflict(instance, other_instance): 
            raise forms.ValidationError("Instances of the same type must not overlap!") 
       else: 
        for instance in instance_dict[obj.foo]: 
         if instance is not obj: 
          if self.instances_conflict(obj, instance): 
           raise forms.ValidationError("Instances of the same type must not overlap!") 

       return super().clean(*args, **kwargs) 

      def instances_conflict(self, instance_1, instance_2): 
       if instance_1.ended is None and instance_2.started > instance_1.started: 
        return True 

       if instance_2.ended is None and instance_1.started > instance_2.started: 
        return True 

       if instance_1.started > instance_2.started and instance_1.started < instance_2.ended: 
        return True 

       if instance_2.started > instance_1.started and instance_2.started < instance_1.ended: 
        return True 

       return False 
     return ChildForm 
+0

你试过我的回答吗? – e4c5

+0

最后我为此使用了自定义表单,并在验证过程中发现了这种情况。约束是我还没有尝试过的东西。然而,我引入了一个预处理钩子来引发异常,如果这种方式超过了模型。理论上这意味着有人可以从psql控制台中解决这个问题。如果我实施限制条件,我会进一步更新。 – xgadam

+0

如果在查询集上调用.update(),它也会中断,因为pre_save信号不会触发。 – e4c5

回答

1

在数据库级别

我使用的数据库是PostgreSQL和AFAIK这不能 在数据库级执行。

那么有很多方法可以在数据库级别用postgresql来做到这一点。你首先应该看看Constraints。尽管像mysql这样的数据库仅限于唯一,外键和主键约束,但postgresql实际上可以输入到列中的值。

检查约束是最一般的约束类型。它允许您 指定某列中的值必须满足布尔型 (真值)表达式。

您可以使用自定义迁移创建约束。但在此之前,您确定这是一个无法通过active, foo上的唯一索引解决的情况?

想到的第二件事是创建一个BEFORE INSERT/UPDATE触发器来在数据写入数据库之前对其进行验证。

使用基于postgesql的方法的优点是,如果有人要使用PGAdmin修改数据,那么约束仍然会执行。

在管理员级别。

您可能会更容易覆盖django模型中的保存方法或管理中的save_model方法来检查约束条件,而不是您当前的方法。

的save_model方法给出的HttpRequest,模型实例,一个 的ModelForm实例,并根据它是否是增加 或改变对象的布尔值。在这里,您可以进行任何保存前或保存后的操作 操作。