Aqui está o meu modelo:
class GroupedModels(models.Model):
other_model_one = models.ForeignKey('app.other_model')
other_model_two = models.ForeignKey('app.other_model')
Essencialmente, o que eu quero é other_model
ser único nesta tabela. Isso significa que, se houver um registro em que other_model_one
id está 123
, não devo permitir que outro registro seja criado com o other_model_two
id como 123
. Eu posso substituir, clean
eu acho, mas eu queria saber se django tem algo embutido.
Estou usando a versão 2.2.5 com PSQL.
Edit: Esta não é uma situação unqiue juntos. Se eu adicionar um registro com other_model_one_id=1
e outro other_model_two_id=2
, não será possível adicionar outro registro com other_model_one_id=2
e comother_model_two_id=1
python
django
django-models
Pittfall
fonte
fonte
Respostas:
Eu explico várias opções aqui, talvez uma delas ou uma combinação possa ser útil para você.
Substituindo
save
Sua restrição é uma regra comercial, você pode substituir o
save
método para manter os dados consistentes:Alterar design
Eu coloquei uma amostra fácil de entender. Vamos supor este cenário:
Agora, você deseja evitar que um time jogue uma partida consigo mesmo, também o time A só pode jogar com o time B pela primeira vez (quase suas regras). Você pode redesenhar seus modelos como:
ManyToManyField.symmetrical
Parece um problema simétrico , o django pode lidar com isso para você. Em vez de criar
GroupedModels
modelo, basta criar um campo ManyToManyField com ele mesmoOtherModel
:Isto é o que o django incorporou para esses cenários.
fonte
match_id
em restrições não iguais, para permitir que as equipes joguem partidas ilimitadas. Basta remover este campo para restringir a reprodução novamente.Não é uma resposta muito satisfatória, mas infelizmente a verdade é que não há como fazer o que você está descrevendo com um simples recurso embutido.
O que você descreveu
clean
funcionaria, mas você deve ter cuidado para chamá-lo manualmente, pois acho que ele é chamado automaticamente somente ao usar o ModelForm. Você pode criar uma restrição de banco de dados complexa, mas que residiria fora do Django e você precisaria lidar com exceções de banco de dados (o que pode ser difícil no Django quando no meio de uma transação).Talvez haja uma maneira melhor de estruturar os dados?
fonte
Já existe uma ótima resposta de dani herrera , mas desejo aprofundar a questão.
Conforme explicado na segunda opção, a solução, conforme exigido pelo OP, é alterar o design e implementar duas restrições exclusivas aos pares. A analogia com as partidas de basquete ilustra o problema de uma maneira muito prática.
Em vez de uma partida de basquete, uso exemplo em jogos de futebol (ou futebol). Um jogo de futebol (como eu chamo
Event
) é jogado por duas equipes (nos meus modelos, uma equipeCompetitor
). Essa é uma relação de muitos para muitos (m:n
), comn
limitado a dois nesse caso específico, o princípio é adequado para um número ilimitado.Aqui está a aparência dos nossos modelos:
Um evento pode ser:
Agora temos que resolver o problema da questão. O Django cria automaticamente uma tabela intermediária entre os modelos com uma relação de muitos para muitos, mas podemos usar um modelo personalizado e adicionar mais campos. Eu chamo esse modelo
Participant
:O
ManyToManyField
possui uma opçãothrough
que nos permite especificar o modelo intermediário. Vamos mudar isso no modeloEvent
:As restrições exclusivas agora limitarão automaticamente o número de competidores por evento a dois (porque existem apenas duas funções: Casa e Visitante ).
Em um evento específico (jogo de futebol), pode haver apenas um time em casa e apenas um time de visitantes. Um clube (
Competitor
) pode aparecer como time da casa ou como visitante.Como gerenciamos agora todas essas coisas no administrador? Como isso:
Adicionamos o
Participant
inline noEventAdmin
. Quando criamos novosEvent
, podemos escolher a equipe da casa e a equipe do visitante. A opçãomax_num
limita o número de entradas a 2, portanto, não é possível adicionar mais de 2 equipes por evento.Isso pode ser refatorado para diferentes casos de uso. Digamos que nossos eventos são competições de natação e, em vez de casa e visitante, temos as faixas de 1 a 8. Apenas refatoramos o
Participant
:Com esta modificação, podemos ter este evento:
título: FINA 2019, final dos 50m costas masculina,
participantes:
// e assim por diante na faixa 5 à faixa 8 (fonte: Wikipedia
Um nadador pode aparecer apenas uma vez no calor e uma pista pode ser ocupada apenas uma vez no calor.
Coloquei o código no GitHub: https://github.com/cezar77/competition .
Mais uma vez, todos os créditos vão para dani herrera. Espero que esta resposta ofereça algum valor agregado aos leitores.
fonte