2016-11-21 5 views
0

У меня есть модель Django с датой начала и окончания. Конечная дата может быть нулевой, и если модель активна, она обрабатывается как o .пользовательская проверка в django admin breaks datetime widget

Каждый экземпляр модели имеет поле foo, которое генерируется динамическим списком. Foo не уникален для всех экземпляров, однако два экземпляра с одинаковыми полями foo не могут быть активны одновременно.

База данных, которую я использую, является PostgreSQL, и afaik это не может быть применено на уровне базы данных. Тогда мой выбор заключался в обеспечении этого на уровне администратора. Когда добавляется новый экземпляр моей модели, я хотел бы подтвердить, что в этот момент не было конфликтующих экземпляров, и вызывают ошибку проверки. Когда я собирался протестировать это локально, виджет datetime не отображается вообще, а только текстовое поле.

Мои вопросы - это лучший способ добиться того, чего я хочу, и если да, то почему отсутствует виджет 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, ограничены ограничениями Unique, Foreign Key и Primary Key, postgresql может фактически CHECK значения, введенные в столбцы.

Ограничение проверки является наиболее общим типом ограничения. Он позволяет вам указать, что значение в определенном столбце должно удовлетворять выражению Boolean (истинностное значение).

Вы можете создать свое ограничение, используя специальную миграцию. Но прежде чем вы уверены, что это ситуация, которая не может быть решена единым уникальным индексом на active, foo?

Вторая вещь, которая приходит на ум - создать триггер ДО НАЧАЛА ВСТАВКИ/ОБНОВЛЕНИЯ, чтобы проверить данные перед их записью в базу данных.

Преимущество использования подхода, основанного на postgesql, заключается в том, что если кто-то должен был модифицировать данные с использованием PGAdmin, ограничения все равно будут соблюдены.

На уровне администратора.

Возможно, было бы намного проще переопределить метод сохранения в модели django или метод save_model в администраторе для проверки ограничений, чем ваш текущий подход.

Метод save_model дается HTTPRequest, экземпляр модели, экземпляр ModelForm и логическое значение, основанное на том, что это добавление или изменение объекта.Здесь вы можете выполнять любые операции до и после сохранения .

Смежные вопросы