0
点赞
收藏
分享

微信扫一扫

12. django关系映射,一对一,一对多,多对多


什么是关系映射

关系型数据库中,通常不会把所有数据都放在同一张表中,常见关系映射有:


  1. 一对一映射 : 一个身份证对应一个人
  2. 一对多映射: 一个班级可以有多个学生
  3. 多对多映射: 一个学生可以报多个课程,一个课程可以有多个学生学习

一对一

语法

一对一 表示现实事物间存在的一对一对应关系。例如一个家庭只有一个户主,一个男人有一个妻子等

语法: OntToOneField(类型,on_delele=xxx)#on_delete :级联删除

class A(model.Model):
....

class B(model.Model):
属性=models.OneToOneField(A,on_delete=xxx)

特殊字段设置

on_delete - 级联删除


  1. models.CASCADE : 级联删除。Django模拟SQL约束ON DELETE CASCADE的行为,并删除包含外键的对象。(注意并没有在数据库层面设置)
  2. models.PROTECT : 抛出ProtectError 会阻止被引用对象的删除;(等同mysql默认的restict)
  3. SET_NULL : 设置ForeignKey Null,需要指定null=True (字段可以为空)
  4. SET_DEFAULT : 将Foreign Key设置为其默认值,必须设置ForeignKey默认值

创建

class Author(models.Model):
# 当有外键指向它的时候,会产生一个对方 小写对象的隐藏属性wife
name=models.CharField("姓名",max_length=10,default='')
emai=models.EmailField("邮箱")
age=models.IntegerField("年龄")
class Meta:
db_table="author"

class Wife(models.Model):
name=models.CharField("姓名",max_length=10,default='')
author=models.OneToOneField(Author,on_delete=models.CASCADE)
class Meta:
db_table="wife"
CREATE TABLE `wife` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) NOT NULL,
`author_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `author_id` (`author_id`),
CONSTRAINT `bookstore_wife_author_id_85e6a2ee_fk_author_id` FOREIGN KEY (`author_id`) REFERENCES `author` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

数据创建

>>> from bookstore.models import *
>>> a1=Author.objects.create(name="王老师",age=20)
>>> a2=Author.objects.create(name="张老师",age=22)
>>> w1=Wife.objects.create(name="王夫人",author=a1)
>>> w1=Wife.objects.create(name="张夫人",author_id=2)
>>>
mysql> select * from author;
+----+-----------+------+-----+
| id | name | emai | age |
+----+-----------+------+-----+
| 1 | 王老师 | | 20 |
| 2 | 张老师 | | 22 |
+----+-----------+------+-----+
2 rows in set (0.00 sec)

查询

  1. 正向查询:直接通过外键属性查询
    通过wife找author
from .models import wife
>>> w1=Wife.objects.get(name="王夫人")
>>> print(w1.name,'的老公是',w1.author.name)
王夫人 的老公是 王老师
  1. 反向查询- 没有外键属性的一方,可以调用方向属性查询到关联的另一方
    反向关联属性为“实例对象.引用类名(小写)”. 如作者的反向引用为 作家对象.wife; 当反向引用不存在时,则会触发异常
>>> a1=Author.objects.get(name="王老师")
>>> a1.wife.name
'王夫人'

一对多

语法

例如 一个学校有多个班级,一个班级有多个学生,一个学生只能属于一个班级。

一对多需要明确出具体角色,在多表上设置外键。

语法: 当一个A类对象可以关联多个B类对象时:

class A(model.Model):
...

class B(model.Model):
属性 = models.ForeignKey('A',on_delete=xxx)

创建

先创建 一 , 再创建 多

class Publisher(models.Model):
'''出版社,代表一'''
name=models.CharField("出版社",max_length=50,default='')
class Meta:
db_table="publisher"


class Book(models.Model):
'''书籍,代表多'''
title = models.CharField("书名", max_length=50, default='', unique=True)
pub = models.CharField("出版社", max_length=100, default="",null=True)
price = models.DecimalField("价格", max_digits=7, decimal_places=2)
market_price = models.DecimalField(
"零售价", max_digits=7, decimal_places=2, default=0.0)
is_active=models.BooleanField("是否活跃",default=True)
publisher=models.ForeignKey(Publisher,on_delete=models.CASCADE)
class Meta:
db_table = "book"

def __str__(self):
return "%s_%s_%s_%s"%(self.title,self.pub,self.price,self.market_price)
CREATE TABLE `book` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(50) NOT NULL,
`pub` varchar(100) DEFAULT NULL,
`price` decimal(7,2) NOT NULL,
`market_price` decimal(7,2) NOT NULL,
`is_active` tinyint(1) NOT NULL,
`publisher_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `title` (`title`),
KEY `book_publisher_id_ac4b162a_fk_publisher_id` (`publisher_id`),
CONSTRAINT `book_publisher_id_ac4b162a_fk_publisher_id` FOREIGN KEY (`publisher_id`) REFERENCES `publisher` (`id`)
)

插入数据

>>> from bookstore.models import *
>>> pub1=Publisher.objects.create(name="清华大学")
>>> pub3=Publisher.objects.create(name="北京大学")
>>> pub2=Publisher.objects.create(name="南昌大学")
>>> s1=Book.objects.create(title="mysql",price=10.2,is_active=True,publisher=pub1)
>>> s2=Book.objects.create(title="oracle",price=10.2,is_active=True,publisher=pub2)
>>> s3=Book.objects.create(title="python",price=10.2,is_active=True,publisher_id=3)

查询

正向查询【通过book 查询 publisher】

通过publisher 属性查询即可

book.publisher

>>> abook=Book.objects.get(id=3)
>>> abook.publisher.name
'南昌大学'

反向查询 【通过publisher查询 book 】

也需要用到反向属性book_set 这个返回也是个object,可以用一堆方法

>>> # 通过book_set 后去pub1对应的多个Book数据对象
>>> books=pub1.book_set.all()
>>> # 也可采用过滤的方式获取
>>> books2=Book.objects.filter(publisher=pub1)
>
>>> for book in books:
... print(book.title)
...
oracle
python
>>> for book in books2:
... print(book.title)
...
oracle
python

多对多

语法

例如每个人有不同学校,每个学校有不同的学生。

实现:

mysql中创建多对多,需要依赖第三张表来实现

Django 中无需手动创建第三张表,Django自动完成

语法:在关联的两个类中任意一个类,增加:

属性=models.ManyToManyField(MyModel)

创建

一个作者可以出版多本书

一本书可以有多个联名作者

from django.db import models

# Create your models here.
class Author(models.Model):
name=models.CharField("作者",max_length=10,default='')

class Book(models.Model):
title = models.CharField("书名", max_length=50, default='', unique=True)
authors = models.ManyToManyField(Author)

可以看到产生了三张表

mysql> show tables;

±---------------------------+

| Tables_in_zaishu |

±---------------------------+

| mtm_author |

| mtm_book |

| mtm_book_authors |

±---------------------------+

17 rows in set (0.02 sec)

mysql> desc mtm_book_authors;
+-----------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| book_id | int(11) | NO | MUL | NULL | |
| author_id | int(11) | NO | MUL | NULL | |
+-----------+---------+------+-----+---------+----------------+
3 rows in set (0.02 sec)
mysql> desc mtm_book;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| title | varchar(50) | NO | UNI | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.02 sec)
mysql> desc mtm_author;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(10) | NO | | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.02 sec)

方案1. 先创建author 再关联book

>>> from mtm.models import *
>>> a1=Author.objects.create(name="王老师")
>>> a2=Author.objects.create(name="张老师")
>>> # 王老师和张老师同时写了一本书
>>> b1 = a1.book_set.create(title="Mysql")
>>> a2.book_set.add(b1)

方案2. 先创建book再关联author

>>> b2 = Book.objects.create(title="Oracle")
>>> a3=b2.authors.create(name="教头")
>>> b2.authors.add(a2)

查询

正向查询

有多对属性的对象查另一方

例如通过Book 查询对应的所有Author. 多对多的属性也是个object

>>> b2.authors.all()
<QuerySet [<Author: Author object (2)>, <Author: Author object (3)>]>
b2.authors.filter(age__gt=80)

反向查询

通过Author 查询对应的所有Book。多对多的属性也是个object

>>> a1.book_set.all()
>>> a1.book_set.filter()



举报

相关推荐

0 条评论