I'm having a little difficulty getting my head around relationships in Django models.
Could someone explain what the difference is between a OneToOne, ManyToMany and ForeignKey?
Well, there's essentially two questions here:
Both of these questions are quite easily answered through a simple Google search, but as I cannot find an exact dupe of this question on SO, I'll go ahead and answer.
Note that in Django, relationships should only be defined on one side of the relationship.
A foreign key relationship is generally known as a many-to-one relationship. Note that the reverse of this relationship is one-to-many (which Django provides tools to access). As the name implies, many objects may be related to one.
Person <--> Birthplace
In this example, a person may only have one birthplace, but a birthplace may be related to many people. Let's look at this example in Django. Say these are our models:
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
You can see that no relations are defined within the
Birthplace model, and a
ForeignKey relationship is defined within the
Person model. Say that we create the following models (obviously not in Python syntax):
Now we can see how Django lets us use these relations (note that the
./manage.py shell is your friend!):
>> 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>]
You can see our created models. Now let's checkout someone's birthplace:
>> person = Person.object.get(name="John Smith") >> person.birthplace <Birthplace: Dallas, Texas> >> person.birthplace.city Dallas
Let's say you want to see all people with a given birthplace. As I said earlier, Django allows you to access reverse relations. By default, Django creates a manager (
RelatedManager) on your model to handle this, named
<model> is your model name in lowercase.
>> place = Birthplace.objects.get(city="Dallas") >> place.person_set.all() [<Person: John Smith>, <Person: Maria Lee>]
Note that we can change the name of this manager by setting the
related_name keyword argument in our model relation. So, we would change the
birthplace field in the
Person model to:
birthplace = models.ForeignKey(Birthplace, related_name="people")
Now, we can access that reverse relationship with a pretty name:
>> place.people.all() [<Person: John Smith>, <Person: Maria Lee>]
A one-to-one relationship is quite similar to a many-to-one relationship, except that it restricts two objects to having a unique relationship. An example of this would be a User and a Profile (which stores information about the user). No two users share the same profile. Let's look at this in Django. I won't bother to define the user model, as Django defines it for us. Do note, however, that Django suggests using
django.contrib.auth.get_user_model() to import the user, so that's what we'll do. The profile model may be defined as follows:
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)
All we need is one user with a profile to test this out in the shell:
Now you may easily access the user's profile from the User model:
>> user = User.objects.all() >> user.username johndt6 >> user.profile <Profile: Kiwi blah_blah> >> user.profile.fruit Kiwi >> profile = Profile.objects.get(user=user) >> profile.user <User: johndt6>
Of course, you may customize the name of the reverse relation using the
related_name argument as above.
Many-to-many relationships can be a bit tricky. Let me start by saying that many-to-many fields are messy, and should be avoided when possible. Given that, there are plenty of situations where a many-to-many relationship makes sense.
A many-to-many relationship between two models defines that zero, one or more objects of the first model may be related to zero, one or more objects of the second model. As an example, let's envision a company that defines their workflow through projects. A project may be related to no orders, only one order, or many orders. An order may be related to no projects, one project, or many. Let's define our models as so:
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))
Note that Django will create a
RelatedManager for the
orders field to access the many-to-many relationship.
Let's create the following objects (in my inconsistent syntax!):
We can access these relationships as follows:
>> 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>]
Note that the NASA order is related to 2 projects, and the US Navy order is related to none. Also note that one project has no orders, and one has multiple.
We may also access the relationship in reverse in the same way we have before:
>> order = Order.objects.filter(customer="NASA") >> order.project_set.all() [<Project: Project 0>, <Project: Project 2>]
Good explanation of db relationships provided by @MarcB