J'ai un peu de mal à comprendre les relations dans les modèles Django.
Quelqu'un pourrait-il expliquer quelle est la différence entre un OneToOne, ManyToMany et ForeignKey?
Eh bien, il y a essentiellement deux questions ici:
Il est assez facile de répondre à ces deux questions via une simple recherche Google, mais comme je ne peux pas trouver une dupe exacte de cette question sur SO, je vais continuer et répondre.
Notez que dans Django, les relations ne doivent être définies que d'un côté de la relation.
Une relation de clé étrangère est généralement connue sous le nom de relation plusieurs-à-un. Notez que l'inverse de cette relation est un-à-plusieurs (qui Django fournit des outils d'accès). Comme son nom l'indique, de nombreux objets peuvent être liés à un seul.
Person >--| Birthplace
^ ^
| |
Many One
Dans cet exemple, une personne peut n'avoir qu'un seul lieu de naissance, mais un lieu de naissance peut être lié à de nombreuses personnes. Regardons cet exemple dans Django. Dites que ce sont nos modèles:
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
Vous pouvez voir qu'aucune relation n'est définie dans le modèle Birthplace
et qu'une relation ForeignKey
est définie dans le modèle Person
. Supposons que nous créons les instances suivantes de nos modèles (évidemment pas dans Python):
Maintenant, nous pouvons voir comment Django nous permet d'utiliser ces relations (notez que ./manage.py Shell
Est votre ami!):
>> from somewhere.models import Birthplace, Person
>> Person.objects.all()
[<Person: John Smith>, <Person: Maria Lee>, <Person: Daniel Lee>]
>> Birthplace.objects.all()
[<Birthplace: Dallas, Texas>, <Birthplace: New York City, New York>]
Vous pouvez voir les instances de modèle que nous avons créées. Voyons maintenant le lieu de naissance de quelqu'un:
>> person = Person.object.get(name="John Smith")
>> person.birthplace
<Birthplace: Dallas, Texas>
>> person.birthplace.city
Dallas
Disons que vous voulez voir toutes les personnes avec un lieu de naissance donné. Comme je l'ai dit plus tôt, Django vous permet d'accéder aux relations inverses. Par défaut, Django crée un gestionnaire ( RelatedManager
) sur votre modèle pour gérer cela, nommé <model>_set
, où <model>
est le nom de votre modèle en minuscules.
>> place = Birthplace.objects.get(city="Dallas")
>> place.person_set.all()
[<Person: John Smith>, <Person: Maria Lee>]
Notez que nous pouvons changer le nom de ce gestionnaire en définissant l'argument du mot clé related_name
Dans notre relation de modèle. Ainsi, nous changerions le champ birthplace
dans le modèle Person
en:
birthplace = models.ForeignKey(Birthplace, related_name="people")
Maintenant, nous pouvons accéder à cette relation inverse avec un joli nom:
>> place.people.all()
[<Person: John Smith>, <Person: Maria Lee>]
Une relation un-à-un est assez similaire à une relation plusieurs-à-un, sauf qu'elle limite deux objets à une relation unique. Un exemple de ceci serait un utilisateur et un profil (qui stocke des informations sur l'utilisateur). Deux utilisateurs ne partagent pas le même profil.
User |--| Profile
^ ^
| |
One One
Regardons cela dans Django. Je ne prendrai pas la peine de définir le modèle utilisateur, comme Django le définit pour nous. Notez cependant que Django suggère d'utiliser Django.contrib.auth.get_user_model()
pour importer l'utilisateur, c'est ce que nous allons faire. Le modèle de profil peut être défini comme suit:
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)
Tout ce dont nous avons besoin, c'est d'un utilisateur avec un profil pour tester cela dans le shell:
Maintenant, vous pouvez facilement accéder au profil de l'utilisateur à partir du modèle utilisateur:
>> user = User.objects.all()[0]
>> user.username
johndt6
>> user.profile
<Profile: Kiwi blah_blah>
>> user.profile.fruit
Kiwi
>> profile = Profile.objects.get(user=user)
>> profile.user
<User: johndt6>
Bien sûr, vous pouvez personnaliser le nom de la relation inverse en utilisant l'argument related_name
Comme ci-dessus.
Les relations plusieurs-à-plusieurs peuvent être un peu délicates. Permettez-moi de commencer par dire que les champs plusieurs-à-plusieurs sont désordonnés et doivent être évités autant que possible. Compte tenu de cela, il existe de nombreuses situations où une relation plusieurs-à-plusieurs a du sens.
Une relation plusieurs-à-plusieurs entre deux modèles définit que zéro, un ou plusieurs objets du premier modèle peuvent être liés à zéro, un ou plusieurs objets du deuxième modèle. À titre d'exemple, imaginons une entreprise qui définit son flux de travail à travers des projets. Un projet peut être lié à aucune commande, à une seule commande ou à plusieurs commandes. Une commande peut être liée à aucun projet, à un projet ou à plusieurs.
Order >--< Project
^ ^
| |
Many Many
Définissons nos modèles comme suit:
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))
Notez que Django créera un RelatedManager
pour le champ orders
pour accéder à la relation plusieurs-à-plusieurs.
Créons les instances suivantes de nos modèles (dans ma syntaxe incohérente!):
Nous pouvons accéder à ces relations comme suit:
>> Project.objects.all()
[<Project: Project 0>, <Project: Project 1>, <Project: Project 2>]
>> 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
[<Order: Spaceship for NASA>]
Project 2
[<Order: Spaceship for NASA>, <Order: Race car for NASCAR>]
Notez que l'ordre de la NASA est lié à 2 projets et que l'ordre de l'US Navy n'est lié à aucun. Notez également qu'un projet n'a pas de commandes et un en a plusieurs.
Nous pouvons également accéder à la relation dans le sens inverse de la même manière que précédemment:
>> order = Order.objects.filter(customer="NASA")[0]
>> order.project_set.all()
[<Project: Project 0>, <Project: Project 2>]
Dans le cas probable où mes diagrammes ASCII sont un peu déroutants, les explications suivantes pourraient être utiles:
>
Ou <
Signifie "à plusieurs"|
Signifie "à un"Donc ... A --| B
Signifie qu'une instance de A peut être liée à une seule instance de B.
Et A --< B
Signifie qu'une instance de A peut être liée à de nombreuses instances de B.
A >--< B
Équivaut à ....
A --< B
A >-- B
Ainsi, chaque "côté" ou direction de la relation peut être lu séparément. Il est juste pratique de les écraser ensemble.
Élargir l'une de ces relations pourrait avoir plus de sens:
+---- John Smith
|
Dallas|-------+---- Jane Doe
|
+---- Joe Smoe
Bonne explication des relations db fourni par @MarcB
page Wikipedia sur la cardinalité