数据表关联关系映射 Relationship Map
在关系型数据库中,通常不会把所有数据都放在同一张表中,这样做会额外占用内存空间,
在关系列数据库中通常用表关联来解决数据库。
用的表关联方式有三种:
一对一映射
如: 一个身份证对应一个人
一对多映射
如: 一个班级可以有多个学生
多对多映射
如: 一个学生可以报多个课程,一个课程可以有多个学生学习
一一对一映射
一对一是表示现实事物间存在的一对一的对应关系。
如:一个家庭只有一个户主,一个男人有一个妻子,一个人有一个唯一的指纹信息等
1.语法:
在关联的两个类中的任何一个类中:
class A(model.Model):
...
class B(model.Model):
属性 = models.OneToOneField(A)
2.用法
(1)创建作家和作家妻子类
# file : xxxxxxxx/models.py
from django.db import models
class Author(models.Model):
'''作家模型类'''
name = models.CharField('作家', max_length=50)
class Wife(models.Model):
'''作家妻子模型类'''
name = models.CharField("妻子", max_length=50)
author = models.OneToOneField(Author) # 增加一对一属性
(2)查询
- 在 Wife 对象中,通过 author 属性找到对应的author对象
- 在 Author 对象中,通过 wife 属性找到对应的wife对象
(3)创始一对一的数据记录
from . import models
author1 = models.Author.objects.create(name='王老师')
wife1 = models.Wife.objects.create(name='王夫人', author=author1) # 关联王老师
author2 = models.Author.objects.create(name='张老师') # 一对一可以没有对应的数据
(4)一对一数据的相互获取
1.正向查询
直接通过关联属性查询即可
# 通过 wife 找 author
from . import models
wife = models.Wife.objects.get(name='王夫人')
print(wife.name, '的老公是', wife.author.name)
2.反向查询
通过反向引用属性查询
实例对象,引用类名(小写),如作家的反向引用为作家对象.wife
当反向引用不存在时,则会触发异常
# 通过 author.wife 引用属性 找 wife,如果没有对应的wife刚触发异常
author1 = models.Author.objects.get(name='王老师')
print(author1.name, '的妻子是', author1.wife.name)
author2 = models.Author.objects.get(name='张老师')
try:
print(author2.name, '的妻子是', author2.wife.name)
except:
print(author2.name, '还没有妻子')
3.作用
主要是解决常用数据不常用数据的存储问题,把经常加载的一个数据放在主表中,不常用数据放在另一个副表中,这样在访问主表数据时不需要加载副表中的数据以提高访问速度提高效率和节省内存空间,如经常把书的内容和书名建成两张表,因为在网站上经常访问书名等信息,但不需要得到书的内容。
二一对多映射
一对多是表示现实事物间存在的一对多的对应关系。
如:一个学校有多个班级,一个班级有多个学生, 一本图书只能属于一个出版社,一个出版社允许出版多本图书
1.用法:当一个A类对象可以关联多个B类对象时
class A(model.Model):
...
class B(model.Model):
属性 = models.ForeignKey(多对一中"一"的模型类, ...)
2.外键类ForeignKey
- 构造函数:ForeignKey(to, on_delete, **options)
- 常用参数:
- on_delete
- models.CASCADE 级联删除。 Django模拟SQL约束ON DELETE CASCADE的行为,并删除包含ForeignKey的对象。
- models.PROTECT 抛出ProtectedError 以阻止被引用对象的删除;
- SET_NULL 设置ForeignKey null;只有null是True才有可能。
- SET_DEFAULT 将ForeignKey设置为其默认值;必须设置ForeignKey的默认值。
- ... 其它参请参考文档 https://docs.djangoproject.com/en/1.11/ref/models/fields/#foreignkey ForeignKey部分
**options
可以是常用的字段选项如:
- null
- unique等
3.示例
有二个出版社对应五本书的情况.
1.清华大学出版社
有书
C++ Java Python
2.北京大学出版社
有书
西游记 水浒
1.定义一对多的类
# file: myorm/models.py
from django.db import models
class Publisher(models.Model):
'''出版社'''
name = models.CharField('名称', max_length=50, unique=True)
class Book(models.Model):
title = models.CharField('书名', max_length=50)
publisher = models.ForeignKey(Publisher, null=True)
----------------------
2.创建一对多的对象
# file: xxxxx/views.py
from . import models
pub1 = models.Publisher.objects.create(name='清华大学出版社')
models.Book.objects.create(title='C++', publisher=pub1)
models.Book.objects.create(title='Java', publisher=pub1)
models.Book.objects.create(title='Python', publisher=pub1)
pub2 = models.Publisher.objects.create(name='北京大学出版社')
models.Book.objects.create(title='西游记', publisher=pub2)
models.Book.objects.create(title='水浒', publisher=pub2)
示例
4.数据查询
通过多查一
# 通过一本书找到对应的出版社
abook = models.Book.objects.get(id=1)
print(abook.title, '的出版社是:', abook.publisher.name)
通过多查一
# 通过出版社查询对应的书
pub1 = models.Publisher.objects.get(name='清华大学出版社')
books = pub1.book_set.all() # 通过book_set 获取pub1对应的多个Book数据对象
# books = models.Book.objects.filter(publisher=pub1) # 也可以采用此方式获取
print("清华大学出版社的书有:")
for book in books:
print(book.title)
三多对多映射
多对多表达对象之间多对多复杂关系,如: 每个人都有不同的学校(小学,初中,高中,...),每个学校都有不同的学生...
1.语法
在关联的两个类中的任意一个类中,增加:
属性 = models.ManyToManyField(MyModel)
2.示例
一个作者可以出版多本图书
一本图书可以被多名作者同时编写
class Author(models.Model):
xxxx xxxx
class Book(models.Model):
xxxx xxxx
authors = models.ManyToManyField(Author)
3.数据查询
通过Book查询对应的所有的Authors
可以通过authors表示对应所有Author的查询对象
book.authors.all() -> 获取 book 对应的所有的author的信息
book.authors.filter(age__gt=80) -> 获取book对应的作者中年龄大于80岁的作者的信息
通过Author查询对应的所有的Books
Django会生成一个属性 book_set 用于表示对对应的book的查询对象相关操作
author.book_set.all()
author.book_set.filter()
author.book_set.create(...) # 创建新书并联作用author
author.book_set.add(book) # 添加已有的书为当前作者author
author.book_set.clear() # 删除author所有并联的书
author.book_set.remove() # 删除所author所有并联的书
#多对多模型
class Author(models.Model):
'''作家模型类'''
name = models.CharField('作家', max_length=50)
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField('书名', max_length=50)
author = models.ManyToManyField(Author, null=True)
def __str__(self):
return self.title
-----------------------
#多对多视图操作
from django.http import HttpResponse
from . import models
def many2many_init(request):
# 创建两人个作者
author1 = models.Author.objects.create(name='王老师')
author2 = models.Author.objects.create(name='张老师')
# 王老师和张老师同时写了一本Python
book11 = author1.book_set.create(title="Python")
author2.book_set.add(book11) #
# 张老师还写了两本书
book21 = author2.book_set.create(title="C") # 创建一本新书"C"
book22 = author2.book_set.create(title="C++") # 创建一本新书"C++"
return HttpResponse("初始化成功")
def show_many2many(request):
authors = models.Author.objects.all()
for auth in authors:
print("作者:", auth.name, '发出版了', auth.book_set.count(), '本书: ')
for book in books:
print(' ', book.title)
print("----显示书和作者的关系----")
books = models.Book.objects.all()
for book in books:
auths = book.author.all()
print(book.title, '的作者是:', '、'.join([str(x.name) for x in auths]))
return HttpResponse("显示成功,请查看服务器端控制台终端")
------------------------
#多对多最终的SQL结果
mysql> select * from myorm2_author;
+----+-----------+
| id | name |
+----+-----------+
| 1 | 王老师 |
| 1 | 张老师 |
+----+-----------+
2 rows in set (0.00 sec)
mysql> select * from myorm2_book;
+----+--------+
| id | title |
+----+--------+
| 13 | Python |
| 14 | C |
| 15 | C++ |
+----+--------+
3 rows in set (0.00 sec)
mysql> select * from myorm2_book_author;
+----+---------+-----------+
| id | book_id | author_id |
+----+---------+-----------+
| 17 | 13 | 11 |
| 20 | 13 | 12 |
| 18 | 14 | 12 |
| 19 | 15 | 12 |
+----+---------+-----------+
4 rows in set (0.00 sec)
多对多模型示例