Ограничение Django DB - разрешить только один объект с полем статуса не "черновик"

У меня есть модель Application с полями bank, mortgage и status. Для одинаковых bank и mortgage я хочу разрешить только одно application с status отличным от DRAFT.

class Application(..):
    bank = ..
    mortgage = ..
    status = .. # draft, active, in_progress etc.

Таким образом, может быть несколько таких применений:

# ok
Application(bank=1, mortgage=2, status='draft') 

# ok, tuple mortgage and bank already exists but but status is draft
Application(bank=1, mortgage=2, status='draft') 

# ok, tuple mortgage and bank already exists but status is active
Application(bank=1, mortgage=2, status='active') 

# ERROR - tuple mortgage and bank already exists and there is already one object with same tuple bank, and non-draft status
Application(bank=1, mortgage=2, status='in_progress') 

# ok - bank is different
Application(bank=3, mortgage=2, status='active') 

Другими словами, я не могу создать приложение с тем же банком, той же ипотекой и статусом не draft, если уже есть приложение с тем же банком, ипотекой и статусом не draft.

Возможно ли сделать такое ограничение? Будет ли CheckConstraint работать?

К сожалению, проверочное ограничение не работает. Контрольное ограничение не может ссылаться за пределы текущего ряда. (См. Документация Примечания.) Похоже, что частичный уникальный индекс тоже не работает. Вы, очевидно, ограничены триггером. Что-то вроде: (см. demo)

create or replace function just_1_non_draft()
  returns  trigger 
  language plpgsql
as $$
begin  
    if exists (select null 
                 from application 
                where (bank, mortage) = (new.bank,new.mortage) 
                  and status != 'draft'        
              ) 
        then 
           raise exception 'Bank %, Mortage % already has non-draft status',new.bank, new.mortage;
    end if; 
    return new;   
end;
$$;

create trigger just_1_non_draft_biur
    before insert or update of status 
        on application
       for each row 
      when (new.status != 'draft') 
   execute function just_1_non_draft();



  

Вы можете использовать UniqueConstraint с условием

class Application(models.Model):
    ...

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['bank', 'mortgage'],
                condition=~models.Q(status='draft'),
                name='unique_non_draft'
            )
        ]

Вышеуказанное создает уникальное ограничение на комбинации bank и mortgage, но только если статус не "черновик"

Вернуться на верх