Aquí está mi modelo:
class GroupedModels(models.Model):
other_model_one = models.ForeignKey('app.other_model')
other_model_two = models.ForeignKey('app.other_model')
Esencialmente, lo que quiero es other_model
que sea único en esta tabla. Eso significa que si hay un registro donde other_model_one
está la identificación 123
, no debería permitir que se cree otro registro con la other_model_two
identificación como 123
. Supongo que puedo anularlo clean
, pero me preguntaba si django tiene algo incorporado.
Estoy usando la versión 2.2.5 con PSQL.
Editar: Esta no es una situación única. Si agrego un registro con other_model_one_id=1
y otro other_model_two_id=2
, no debería poder agregar otro registro con other_model_one_id=2
y otroother_model_two_id=1
python
django
django-models
Pittfall
fuente
fuente
Respuestas:
Aquí explico varias opciones, tal vez una de ellas o una combinación puede ser útil para usted.
Primordial
save
Su restricción es una regla de negocio, puede anular el
save
método para mantener los datos consistentes:Cambiar diseño
Puse una muestra fácil de entender. Supongamos este escenario:
Ahora, quieres evitar que un equipo juegue un partido consigo mismo y el equipo A solo puede jugar con el equipo B por una vez (casi tus reglas). Puede rediseñar sus modelos como:
ManyToManyField.symmetrical
Esto parece un problema simétrico , django puede manejarlo por usted. En lugar de crear un
GroupedModels
modelo, simplemente haga un campo ManyToManyField consigo mismo enOtherModel
:Esto es lo que django tiene incorporado para estos escenarios.
fuente
match_id
incluí en la restricción de unike, para permitir que los equipos jueguen partidos ilimitados. Simplemente elimine este campo para restringir el juego nuevamente.No es una respuesta muy satisfactoria, pero desafortunadamente la verdad es que no hay forma de hacer lo que está describiendo con una característica incorporada simple.
Lo que describiste
clean
funcionaría, pero debes tener cuidado de llamarlo manualmente, ya que creo que solo se llama automáticamente al usar ModelForm. Es posible que pueda crear una restricción de base de datos compleja, pero que viviría fuera de Django y tendría que manejar las excepciones de la base de datos (lo que puede ser difícil en Django cuando está en medio de una transacción).¿Quizás haya una mejor manera de estructurar los datos?
fuente
Ya hay una gran respuesta de dani herrera , sin embargo, deseo dar más detalles al respecto.
Como se explica en la segunda opción, la solución requerida por el OP es cambiar el diseño e implementar dos restricciones únicas por pares. La analogía con los partidos de baloncesto ilustra el problema de una manera muy práctica.
En lugar de un partido de baloncesto, utilizo ejemplos con juegos de fútbol (o fútbol). Un juego de fútbol (que yo llamo
Event
) es jugado por dos equipos (en mis modelos es un equipoCompetitor
). Esta es una relación de muchos a muchos (m:n
), con unn
límite de dos en este caso particular, el principio es adecuado para un número ilimitado.Así es como se ven nuestros modelos:
Un evento podría ser:
Ahora tenemos que resolver el problema a partir de la pregunta. Django crea automáticamente una tabla intermedia entre los modelos con una relación de muchos a muchos, pero podemos usar un modelo personalizado y agregar más campos. Yo llamo a ese modelo
Participant
:El
ManyToManyField
tiene una opciónthrough
que nos permite especificar el modelo intermedio. Cambiemos eso en el modeloEvent
:Las restricciones únicas ahora limitarán automáticamente el número de competidores por evento a dos (porque solo hay dos roles: Hogar y Visitante ).
En un evento particular (juego de fútbol) solo puede haber un equipo local y solo un equipo visitante. Un club (
Competitor
) puede aparecer como equipo local o como equipo visitante.¿Cómo gestionamos ahora todas estas cosas en el administrador? Me gusta esto:
Hemos agregado
Participant
como en línea en elEventAdmin
. Cuando creamos nuevosEvent
, podemos elegir el equipo local y el equipo visitante. La opciónmax_num
limita el número de entradas a 2, por lo tanto, no se pueden agregar más de 2 equipos por evento.Esto se puede refactorizar para diferentes casos de uso. Digamos que nuestros eventos son competiciones de natación y, en lugar de casa y visitante, tenemos carriles 1 a 8. Simplemente refactorizamos
Participant
:Con esta modificación podemos tener este evento:
título: FINA 2019, final masculino de espalda de 50 m,
Participantes:
// y así sucesivamente del carril 5 al carril 8 (fuente: Wikipedia
Un nadador puede aparecer solo una vez en celo, y un carril puede estar ocupado solo una vez en celo.
Puse el código en GitHub: https://github.com/cezar77/competition .
Una vez más, todos los créditos van a Dani Herrera. Espero que esta respuesta brinde algún valor agregado a los lectores.
fuente