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_modelque sea único en esta tabla. Eso significa que si hay un registro donde other_model_oneestá la identificación 123, no debería permitir que se cree otro registro con la other_model_twoidentificació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=1y otro other_model_two_id=2, no debería poder agregar otro registro con other_model_one_id=2y 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
saveSu restricción es una regla de negocio, puede anular el
savemé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.symmetricalEsto parece un problema simétrico , django puede manejarlo por usted. En lugar de crear un
GroupedModelsmodelo, simplemente haga un campo ManyToManyField consigo mismo enOtherModel:Esto es lo que django tiene incorporado para estos escenarios.
fuente
match_idincluí 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
cleanfuncionarí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 unnlí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:Participante de clase (modelos.Modelo): ROLES = ( ('H', 'Inicio'), ('V', 'Visitante'), ) event = models.ForeignKey (Evento, on_delete = models.CASCADE) competitor = models.ForeignKey (Competitor, on_delete = models.CASCADE) role = models.CharField (max_length = 1, options = ROLES) clase Meta: unique_together = ( ('evento', 'rol'), ('evento', 'competidor'), ) def __str __ (self): return '{} - {}'. format (self.event, self.get_role_display ())El
ManyToManyFieldtiene una opciónthroughque 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
Participantcomo en línea en elEventAdmin. Cuando creamos nuevosEvent, podemos elegir el equipo local y el equipo visitante. La opciónmax_numlimita 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