¿Cuál es la diferencia entre un campo OneToOne, ManyToMany y ForeignKey en Django?

Estoy teniendo un poco de dificultad para entender las relaciones en los modelos de Django.

¿Podría alguien explicar cuál es la diferencia entre OneToOne, ManyToMany y ForeignKey?

Bueno, hay esencialmente dos preguntas aquí:

  1. ¿Cuál es la diferencia (en general) entre uno a uno, muchos a muchos y relaciones clave externas?
  2. ¿Cuáles son sus diferencias específicas a Django.

Ambas preguntas se responden con bastante facilidad a través de una simple búsqueda en Google, pero como no puedo encontrar un duplicado exacto de esta pregunta en SO, seguiré adelante y responderé.

Tenga en cuenta que en Django, las relaciones solo deben definirse en un lado de la relación.


Clave externa

Una relación de clave externa se conoce generalmente como una relación de muchos a uno. Tenga en cuenta que lo contrario de esta relación es de uno a muchos (a la que Django proporciona herramientas para acceder). Como su nombre lo indica, muchos objetos pueden estar relacionados con uno.

Person >--| Birthplace ^ ^ | | Many One 

En este ejemplo, una persona puede tener solo un lugar de nacimiento, pero un lugar de nacimiento puede estar relacionado con muchas personas. Veamos este ejemplo en Django. Digamos que estos son nuestros modelos:

 class Birthplace(models.Model): city = models.CharField(max_length=75) state = models.CharField(max_length=25) def __unicode__(self): return "".join(self.city, ", ", self.state) class Person(models.Model): name = models.CharField(max_length=50) birthplace = models.ForeignKey(Birthplace) def __unicode__(self): return self.name 

Puede ver que no hay relaciones definidas dentro del modelo de Birthplace , y una relación ForeignKey está definida dentro del modelo de Person . Digamos que creamos las siguientes instancias de nuestros modelos (obviamente no en la syntax de Python):

  • Lugar de nacimiento: Dallas, Texas
  • Lugar de nacimiento: Nueva York, Nueva York.
  • Persona: John Smith, Lugar de nacimiento: (Dallas, Texas)
  • Persona: Maria Lee, Lugar de nacimiento: (Dallas, Texas)
  • Persona: Daniel Lee, Lugar de nacimiento: (Nueva York, Nueva York)

Ahora podemos ver cómo Django nos permite usar estas relaciones (¡note que ./manage.py shell es su amigo!):

 >> from somewhere.models import Birthplace, Person >> Person.objects.all() [, , ] >> Birthplace.objects.all() [, ] 

Puedes ver las instancias del modelo que creamos. Ahora vamos a ver el lugar de nacimiento de alguien:

 >> person = Person.object.get(name="John Smith") >> person.birthplace  >> person.birthplace.city Dallas 

Digamos que quieres ver a todas las personas con un lugar de nacimiento determinado. Como dije anteriormente, Django te permite acceder a relaciones inversas. De forma predeterminada, Django crea un administrador ( RelatedManager ) en su modelo para manejar esto, denominado _set , donde es el nombre de su modelo en minúscula.

 >> place = Birthplace.objects.get(city="Dallas") >> place.person_set.all() [, ] 

Tenga en cuenta que podemos cambiar el nombre de este administrador estableciendo el argumento de palabra clave related_name en nuestra relación de modelo. Por lo tanto, cambiaríamos el campo de birthplace en el modelo de Person a:

 birthplace = models.ForeignKey(Birthplace, related_name="people") 

Ahora, podemos acceder a esa relación inversa con un nombre bonito:

 >> place.people.all() [, ] 

Doce y cincuenta y nueve de la noche

Una relación de uno a uno es bastante similar a una relación de muchos a uno, excepto que restringe dos objetos a tener una relación única. Un ejemplo de esto sería un usuario y un perfil (que almacena información sobre el usuario). No hay dos usuarios que compartan el mismo perfil.

 User |--| Profile ^ ^ | | One One 

Veamos esto en Django. No me molestaré en definir el modelo de usuario, como lo define Django para nosotros. Sin embargo, tenga en cuenta que Django sugiere usar django.contrib.auth.get_user_model() para importar al usuario, así que eso es lo que haremos. El modelo de perfil se puede definir de la siguiente manera:

 class Profile(models.Model): user = models.OneToOneField(settings.AUTH_USER_MODEL) # Note that Django suggests getting the User from the settings for relationship definitions fruit = models.CharField(max_length=50, help_text="Favorite Fruit") facebook = models.CharField(max_length=100, help_text="Facebook Username") def __unicode__(self): return "".join(self.fruit, " ", self.facebook) 

Todo lo que necesitamos es un usuario con un perfil para probar esto en el shell:

  • Usuario: johndt6
  • Perfil: usuario: johndt6, “Kiwi”, “blah_blah”

Ahora puede acceder fácilmente al perfil del usuario desde el modelo de usuario:

 >> user = User.objects.all()[0] >> user.username johndt6 >> user.profile  >> user.profile.fruit Kiwi >> profile = Profile.objects.get(user=user) >> profile.user  

Por supuesto, puede personalizar el nombre de la relación inversa usando el argumento related_name como se related_name anteriormente.


Muchos a muchos

Las relaciones de muchos a muchos pueden ser un poco difíciles. Permítanme comenzar diciendo que los campos de muchos a muchos son confusos, y deben evitarse cuando sea posible. Dado esto, hay muchas situaciones en las que una relación de muchos a muchos tiene sentido.

Una relación de muchos a muchos entre dos modelos define que cero, uno o más objetos del primer modelo pueden estar relacionados con cero, uno o más objetos del segundo modelo. Como ejemplo, imaginemos una empresa que define su flujo de trabajo a través de proyectos. Un proyecto puede estar relacionado con ningún pedido, solo un pedido o muchos pedidos. Una orden puede estar relacionada con ningún proyecto, un proyecto o muchos.

 Order >--< Project ^ ^ | | Many Many 

Definamos nuestros modelos como tal:

 class Order(models.Model): product = models.CharField(max_length=150) # Note that in reality, this would probably be better served by a Product model customer = models.CharField(max_length=150) # The same may be said for customers def __unicode__(self): return "".join(self.product, " for ", self.customer) class Project(models.Model): orders = models.ManyToManyField(Order) def __unicode__(self): return "".join("Project ", str(self.id)) 

Tenga en cuenta que Django creará un RelatedManager para que el campo de orders acceda a la relación de muchos a muchos.

Vamos a crear las siguientes instancias de nuestros modelos (en mi syntax inconsistente):

  • Orden: "Nave espacial", "NASA"
  • Orden: "Submarino", "Marina de los Estados Unidos"
  • Orden: "Coche de carreras", "NASCAR"
  • Proyecto: pedidos: []
  • Proyecto: órdenes: [(Orden: "Nave espacial", "NASA")]
  • Proyecto: órdenes: [(Orden: "Nave espacial", "NASA"), (Orden: "Coche de carreras", "NASCAR")]

Podemos acceder a estas relaciones de la siguiente manera:

 >> Project.objects.all() [, , ] >> for proj in Project.objects.all(): .. print(proj) .. proj.orders.all() # Note that we must access the `orders` .. # field through its manager .. print("") Project 0 [] Project 1 [] Project 2 [, ] 

Tenga en cuenta que la orden de la NASA está relacionada con 2 proyectos, y la orden de la Marina de los EE. UU. Está relacionada con ninguno. También tenga en cuenta que un proyecto no tiene órdenes, y uno tiene múltiples.

También podemos acceder a la relación a la inversa de la misma manera que antes:

 >> order = Order.objects.filter(customer="NASA")[0] >> order.project_set.all() [, ] 

Guia de cardinalidad ASCII

En el caso probable de que mis diagtwigs ASCII sean un poco confusos, las siguientes explicaciones pueden ser útiles:

  • > o < significa "para muchos"
  • | significa "a uno"

Entonces ... A --| B A --| B significa que una instancia de A se puede relacionar con solo UNA instancia de B.

Y A --< B significa que una instancia de A puede estar relacionada con MUCHAS instancias de B.

A >--< B es equivalente a ....

 A --< B A >-- B 

Por lo tanto, cada "lado" o dirección de la relación se puede leer por separado. Es conveniente aplastarlos juntos.

Expandir una de estas relaciones podría tener más sentido:

  +---- John Smith | Dallas|-------+---- Jane Doe | +---- Joe Smoe 

Recursos

Buena explicación de las relaciones db proporcionadas por @MarcB

Página de Wikipedia sobre la cardinalidad.

Django Docs:

models.ForeignKey

models.OneToOneField

models.ManyToManyField

Relaciones Uno a Uno

Relaciones de muchos a muchos