Django 中的 OneToOne、ManyToMany 和 ForeignKey 字段有什么区别?

问题描述

我很难理解 Django 模型中的关系.

I'm having a little difficulty getting my head around relationships in Django models.

有人能解释一下 OneToOne、ManyToMany 和 ForeignKey 之间的区别吗?

Could someone explain what the difference is between a OneToOne, ManyToMany and ForeignKey?


解决方案

嗯,这里基本上有两个问题:

Well, there's essentially two questions here:

  1. 一对一、多对多和外键关系之间有什么区别(通常)
  2. 它们在 Django 上有何不同.

这两个问题都可以通过简单的谷歌搜索很容易地回答,但由于我在 SO 上找不到这个问题的确切欺骗,我会继续回答.

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.

请注意,在 Django 中,关系只能定义在关系的一侧.

Note that in Django, relationships should only be defined on one side of the relationship.

外键关系通常称为多对一关系.请注意,这种关系的反面是一对多(Django 提供了访问工具).顾名思义,许多对象可能与一个对象相关.

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
   ^           ^
   |           |
  Many        One 

在这个例子中,一个人可能只有一个出生地,但一个出生地可能与很多人有关.让我们看一下 Django 中的这个例子.假设这些是我们的模型:

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

您可以看到 Birthplace 模型中没有定义任何关系,而 Person 模型中定义了 ForeignKey 关系.假设我们创建了以下模型实例(显然不是 Python 语法):

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 instances of our models (obviously not in Python syntax):

  • 出生地:德克萨斯州达拉斯
  • 出生地:纽约州纽约市
  • 人:约翰·史密斯,出生地:(德克萨斯州达拉斯)
  • 人:Maria Lee,出生地:(德克萨斯州达拉斯)
  • 人:Daniel Lee,出生地:(纽约市,纽约)

现在我们可以看到 Django 如何让我们使用这些关系(注意 ./manage.py shell 是你的朋友!):

Now we can see how Django lets us use these relations (note that ./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 the model instances we created. Now let's checkout someone's birthplace:

>> person = Person.object.get(name="John Smith")
>> person.birthplace
<Birthplace: Dallas, Texas>
>> person.birthplace.city
Dallas

假设您想查看具有给定出生地的所有人.正如我之前所说,Django 允许您访问反向关系.默认情况下,Django 创建一个管理器 (RelatedManager) 来处理这个问题,命名为 <model>_set,其中 <model> 是你的小写的型号名称.

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>_set, where <model> is your model name in lowercase.

>> place = Birthplace.objects.get(city="Dallas")
>> place.person_set.all()
[<Person: John Smith>, <Person: Maria Lee>]

请注意,我们可以通过在模型关系中设置 related_name 关键字参数来更改此管理器的名称.因此,我们将 Person 模型中的 birthplace 字段更改为:

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>]

<小时>

一对一

一对一关系与多对一关系非常相似,只是它将两个对象限制为具有唯一关系.这方面的一个例子是用户和个人资料(存储有关用户的信息).没有两个用户共享相同的个人资料.


One-to-one

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.

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

让我们在 Django 中看看这个.我不会费心去定义用户模型,因为 Django 为我们定义了它.但是请注意,Django 建议使用 django.contrib.auth.get_user_model() 来导入用户,这就是我们要做的.配置文件模型可以定义如下:

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)

我们只需要一个具有配置文件的用户就可以在 shell 中进行测试:

All we need is one user with a profile to test this out in the shell:

  • 用户:johndt6
  • 个人资料:用户:johndt6、Kiwi"、blah_blah"

现在您可以轻松地从 User 模型访问用户的个人资料:

Now you may easily access the user's profile from the User model:

>> 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>

当然,你也可以使用上面的 related_name 参数自定义反向关系的名称.

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.

Order >--< Project
  ^           ^
  |           |
 Many        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))

请注意,Django 将为 orders 字段创建一个 RelatedManager 以访问多对多关系.

Note that Django will create a RelatedManager for the orders field to access the many-to-many relationship.

让我们创建模型的以下实例(我的语法不一致!):

Let's create the following instances of our models (in my inconsistent syntax!):

  • 顺序:宇宙飞船"、NASA"
  • 订单:潜艇"、美国海军"
  • 订单:赛车"、NASCAR"
  • 项目:订单:[]
  • 项目:订单:[(Order: "Spaceship", "NASA")]
  • 项目:订单:[(订单:Spaceship"、NASA")、(订单:赛车"、NASCAR")]

我们可以按如下方式访问这些关系:

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>]

请注意,NASA 订单与 2 个项目相关,而美国海军订单与任何项目无关.另请注意,一个项目没有订单,一个项目有多个.

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")[0]
>> order.project_set.all()
[<Project: Project 0>, <Project: Project 2>]

<小时>

ASCII 基数指南

在我的 ASCII 图表可能有点混乱的情况下,以下解释可能会有所帮助:


ASCII Cardinality Guide

In the likely case that my ASCII diagrams are a bit confusing, the following explanations might be helpful:

  • >< 表示对多个"
  • | 意思是合一"
  • > or < means "to many"
  • | means "to one"

所以... A --|B 表示 A 的一个实例只能与 B 的一个实例相关.

So... A --| B means an instance of A can be related to only ONE instance of B.

A --<B 表示 A 的一个实例可以与 B 的许多实例相关.

And A --< B means an instance of A can be related to MANY instances of B.

A >--<B 等价于....

A --< B
A >-- B

因此,关系的每个边"或方向都可以单独阅读.把它们挤在一起很方便.

Thus, each "side" or direction of the relationship can be read separately. It's just convenient to squish them together.

扩展其中一种关系可能更有意义:

Expanding one of these relationships might make more sense:

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

资源

对数据库关系的良好解释 由@MarcB 提供

Resources

Good explanation of db relationships provided by @MarcB

关于基数的维基百科页面

models.ForeignKey

models.OneToOneField

models.ManyToManyField

一对一关系

多对多关系

相关文章