我对Django模型中的关系有一些困惑。
能否有人解释一下OneToOne、ManyToMany和ForeignKey之间的区别?
我对Django模型中的关系有一些困惑。
能否有人解释一下OneToOne、ManyToMany和ForeignKey之间的区别?
这里基本上有两个问题:
这两个问题都可以通过简单的Google搜索很容易地得到答案,但由于我无法在SO上找到确切的重复问题,因此我会回答这个问题。
请注意,在Django中,关系应该只定义在关系的一侧。
外键关系通常称为多对一关系。请注意,这种关系的反向是一对多关系(Django提供了访问工具)。正如名称所示,多个对象可以与一个对象相关联。
Person >--| Birthplace
^ ^
| |
Many One
在这个例子中,一个人可能只有一个出生地,但一个出生地可能与多个人相关联。现在我们来看一下 Django 中的这个例子。假设这是我们的模型:
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语法):
现在我们可以看到Django如何让我们使用这些关系(请注意,./ manage.py shell
是您的好朋友!):
>> 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>]
您可以查看我们创建的模型实例。现在让我们检查某人的出生地:
>> person = Person.object.get(name="John Smith")
>> person.birthplace
<Birthplace: Dallas, Texas>
>> person.birthplace.city
Dallas
假设你想查看所有拥有相同出生地的人员。正如我之前所说,Django 允许您访问反向关系。默认情况下,Django 会在您的模型上创建一个管理器 (RelatedManager
) 来处理此操作,该管理器名为 <model>_set
,其中 <model>
是小写的模型名称。
>> place = Birthplace.objects.get(city="Dallas")
>> place.person_set.all()
[<Person: John Smith>, <Person: Maria Lee>]
请注意,我们可以通过在模型关系中设置related_name
关键字参数来更改此管理器的名称。因此,我们将在Person
模型中更改birthplace
字段为:
birthplace = models.ForeignKey(Birthplace, related_name="people")
现在,我们可以使用一个漂亮的名称访问该反向关系:
>> place.people.all()
[<Person: John Smith>, <Person: Maria Lee>]
一对一关系与多对一关系非常相似,不同之处在于它限制了两个对象之间具有唯一的关系。例如,用户和个人资料(存储用户信息)之间的关系就是一对一的。没有两个用户共享同一个个人资料。
User |--| Profile
^ ^
| |
One One
让我们在Django中来看看这个。我不会费力地定义用户模型,因为Django已经为我们定义好了。但是请注意,Django建议使用django.contrib.auth.get_user_model()
来导入用户,所以我们将这样做。简介模型可以定义如下:
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 中测试一下:
现在您可以轻松地从 User 模型中访问该用户的个人资料:
>> 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
参数自定义反向关系的名称。
多对多关系可能有点棘手。首先我要说的是,多对多字段很麻烦,应尽可能避免使用。但是,在许多情况下,多对多关系是有意义的。
两个模型之间的多对多关系定义了第一个模型的零个、一个或多个对象与第二个模型的零个、一个或多个对象之间的关系。例如,假设一个公司通过项目来定义他们的工作流程。一个项目可能与零个、一个或多个订单相关联。一个订单可能与零个、一个或多个项目相关联。
Order >--< Project
^ ^
| |
Many Many
我们定义我们的模型如下:
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”来访问多对多关系。>> 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个项目有关,而美国海军订单与任何项目无关。还要注意一个项目没有订单,而另一个项目有多个订单。
我们也可以以同样的方式反向访问关系:
>> order = Order.objects.filter(customer="NASA")[0]
>> order.project_set.all()
[<Project: Project 0>, <Project: Project 2>]
如果我的 ASCII 图表有些令人困惑,下面的解释可能会有所帮助:
>
或 <
表示 "到许多个"|
表示 "到一个"因此...A --| B
意味着 A 的一个实例只能与 B 的一个实例相关联。
而 A --< B
意味着 A 的一个实例可以与许多个 B 的实例相关联。
A >--< B
等同于....
A --< B
A >-- B
因此,每个关系的“方向”都可以单独阅读。将它们合并在一起只是为了方便。
扩展其中一个关系可能更有意义:
+---- John Smith
|
Dallas|-------+---- Jane Doe
|
+---- Joe Smoe
ManyToManyField
会在数据库中创建一个额外的表,其中包含3个字段:主键和2个连接表的引用(table1_id,table2_id)。ManyToManyField
非常适合标签(例如,如果您想使用标签标记特定的项目/产品/职位)。 - 0leg