Django ORM 一对一,一对多,多对多关系

关联的对象

当你在一个模型中定义一个关联关系时(例如,ForeignKey、 OneToOneField 或ManyToManyField),该模型的实例将带有一个方便的API 来访问关联的对象。

利用本页顶部的模型,一个Entry 对象e 可以通过blog 属性e.blog 获取关联的Blog 对象。

(在幕后,这个功能是通过Python 的描述器实现的。这应该不会对你有什么真正的影响,但是这里我们指出它以满足你的好奇)。

Django 还会创建API 用于访问关联关系的另一头 —— 从关联的模型访问定义关联关系的模型。例如,Blog 对象b 可以通过entry_set 属性 b.entry_set.all()访问与它关联的所有Entry 对象。

这一节中的所有示例都将使用本页顶部定义的Blog、 Author 和Entry 模型。

一对多关系

前向查询

如果一个模型具有ForeignKey,那么该模型的实例将可以通过属性访问关联的(外部)对象。

例如:

>>> e = Entry.objects.get(id=2)
>>> e.blog # Returns the related Blog object.

你可以通过外键属性获取和设置。和你预期的一样,对外键的修改不会保存到数据库中直至你调用save()例如:

>>> e = Entry.objects.get(id=2)
>>> e.blog = some_blog
>>> e.save()

如果ForeignKey 字段有null=True 设置(即它允许NULL 值),你可以分配None 来删除对应的关联性。例如:

>>> e = Entry.objects.get(id=2)
>>> e.blog = None
>>> e.save() # "UPDATE blog_entry SET blog_id = NULL ...;"

一对多关联关系的前向访问在第一次访问关联的对象时被缓存。以后对同一个对象的外键的访问都使用缓存。例如:

>>> e = Entry.objects.get(id=2)
>>> print(e.blog)  # Hits the database to retrieve the associated Blog.
>>> print(e.blog)  # Doesn't hit the database; uses cached version.

注意select_related() 查询集方法递归地预填充所有的一对多关系到缓存中。例如:

>>> e = Entry.objects.select_related().get(id=2)
>>> print(e.blog)  # Doesn't hit the database; uses cached version.
>>> print(e.blog)  # Doesn't hit the database; uses cached version.

反向查询

如果模型I有一个ForeignKey,那么该ForeignKey 所指的模型II实例可以通过一个管理器返回前面有ForeignKey的模型I的所有实例。默认情况下,这个管理器的名字为foo_set,其中foo 是源模型的小写名称。管理器返回的查询集可以用上一节提到的方式进行过滤和操作。

例如:

>>> b = Blog.objects.get(id=1)
>>> b.entry_set.all() # Returns all Entry objects related to Blog.

# b.entry_set is a Manager that returns QuerySets.
>>> b.entry_set.filter(headline__contains='Lennon')
>>> b.entry_set.count()

你可以在ForeignKey 定义时设置related_name 参数来覆盖foo_set 的名称。例如,如果Entry 模型改成blog = ForeignKey(Blog, related_name='entries'),那么上面的示例代码应该改成这样:

>>> b = Blog.objects.get(id=1)
>>> b.entries.all() # Returns all Entry objects related to Blog.

# b.entries is a Manager that returns QuerySets.
>>> b.entries.filter(headline__contains='Lennon')
>>> b.entries.count()

使用自定义的反向管理器

New in Django 1.7.

默认情况下,用于反向关联关系的RelatedManager 是该模型默认管理器 的子类。如果你想为一个查询指定一个不同的管理器,你可以使用下面的语法:

from django.db import models

class Entry(models.Model):
    #...
    objects = models.Manager()  # Default Manager
    entries = EntryManager()    # Custom Manager

b = Blog.objects.get(id=1)
b.entry_set(manager='entries').all()

如果EntryManager 在它的get_queryset() 方法中使用默认的过滤,那么该过滤将适用于all() 调用。

当然,指定一个自定义的管理器还可以让你调用自定义的方法:

b.entry_set(manager='entries').is_published()

处理关联对象的其它方法

除了在上面”获取对象“一节中定义的查询集 方法之外,ForeignKey 管理器 还有其它方法用于处理关联的对象集合。下面是每个方法的大概,完整的细节可以在关联对象参考 中找到。

add(obj1, obj2, ...)
添加一指定的模型对象到关联的对象集中。
create(**kwargs)
创建一个新的对象,将它保存并放在关联的对象集中。 返回新创建的对象。
remove(obj1, obj2, ...)
从关联的对象集中删除指定的模型对象。
clear()
从关联的对象集中删除所有的对象。

若要一次性给关联的对象集赋值,只需要给它赋值一个可迭代的对象。这个可迭代的对象可以包含对象的实例,或者一个主键值的列表。例如:

b = Blog.objects.get(id=1)
b.entry_set = [e1, e2]

在这个例子中,e1 和e2 可以是Entry 实例,也可以是主键的整数值。

如果有clear() 方法,那么在将可迭代对象中的成员添加到集合中之前,将从entry_set 中删除所有已经存在的对象。如果没有clear() 方法,那么将直接添加可迭代对象中的成员而不会删除所有已存在的对象。

这一节中提到的每个”反向“操作都会立即对数据库产生作用。每个添加、创建和删除操作都会立即并自动保存到数据库中。

多对多关系

多对多关系的两端都会自动获得访问另一端的API。这些API 的工作方式与上面提到的“方向”一对多关系一样。

唯一的区别在于属性的名称:定义 ManyToManyField 的模型使用该字段的属性名称,而“反向”模型使用源模型的小写名称加上'_set' (和一对多关系一样)。

一个例子可以让它更好理解:

e = Entry.objects.get(id=3)
e.authors.all() # Returns all Author objects for this Entry.
e.authors.count()
e.authors.filter(name__contains='John')

a = Author.objects.get(id=5)
a.entry_set.all() # Returns all Entry objects for this Author.

类似ForeignKeyManyToManyField 可以指定related_name在上面的例子中,如果Entry 中的ManyToManyField 指定related_name='entries',那么Author 实例将使用 entries 属性而不是entry_set

一对一关系

一对一关系与多对一关系非常相似。如果你在模型中定义一个OneToOneField,该模型的实例将可以通过该模型的一个简单属性访问关联的模型。

例如:

class EntryDetail(models.Model):
    entry = models.OneToOneField(Entry)
    details = models.TextField()

ed = EntryDetail.objects.get(id=2)
ed.entry # Returns the related Entry object.

在“反向”查询中有所不同。一对一关系中的关联模型同样具有一个管理器对象,但是该管理器表示一个单一的对象而不是对象的集合:

e = Entry.objects.get(id=2)
e.entrydetail # returns the related EntryDetail object

如果没有对象赋值给这个关联关系,Django 将引发一个DoesNotExist 异常。

实例可以赋值给反向的关联关系,方法和正向的关联关系一样:

e.entrydetail = ed

你可能感兴趣的:(Django ORM 一对一,一对多,多对多关系)