2016-11-21 59 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實例,並根據它是否是增加 或改變對象的布爾值。在這裏,您可以進行任何保存前或保存後的操作 操作。