模型关系
Django模型默认在关系数据库系统(RDBMS)上的关系。
Django遵循三种模型关系:
- 一对一关系
- 一对多关系
- 多对多关系
一对一关系
OneToOneField
一个表中的一个记录与另一个表中的一个记录相关联。并且仅与另一个表的记录相关联。
比如用户表与用户扩展表
class User(models.Model):
username = models.CharField(max_length=20, default='')
password = models.CharField(max_length=200, default='')
def __str__(self):
return " User: {}".format(self.username)
class UserExtension(models.Model):
nickname = models.CharField(max_length=50)
sex = models.CharField(max_length=50)
age = models.IntegerField()
user = models.OneToOneField("User", on_delete=models.CASCADE)
其实是为我们默添加了一个user_id
来和user
表来进行关联。并且这个外键数据表中必须是唯一的。来保证一对一。
其本质也是一个外键,只不过这个外键有了一个唯一约束,来实现一对一。
如果想要反向引用 模型的名字转化为小写的名字来访问比如模型类UserExtension 则查询 xxx.userextension
查询
# 查看扩展表中的对应用户信息
ex = UserExtension.objects.first()
print(ex.user)
# 通过用户获取扩展表信息
user = User.objects.first() # 获取一个用户
print(user)
res = user.userextension
print(res)
- 当然也可以为OneToOneField添加一个related_name
后续的操作就可以通过自定义的属性来获取数据。
user = User.objects.first() # 获取一个用户
print(user)
res = user.gugu
print(res)
获取某个分类下的所有文章
category = Category.objects.all()
category.article_set.all() # 字段_set
一对多关系
ForeignKey
一个表的一个记录可以与另一个表一个或多个记录相关联。
实现方式
Django为我们专门提供了一个字段OneToOneField来实现一对一操作
多对一的关系,通常被称为外键。
在数据库设计中,多对一(或一对多)是指一个或多个实体之间的关系,其中单个实体可以具有许多连接的实体。
外键需要两个位置参数,一个是关联的模型,另一个是on_delete选项(使用外键引用数据的被删除了,采取什么策略。)。
实际上,在目前版本中,on_delete选项也可以不设置,但Django极力反对如此,因此在Django2.0版本后,该选项会设置为必填
在MySQL中,表有两种引擎,一种是InnoDB,另外一种是msisam
如果使用的InnoDB,是支持外键约束的。
Tip: 外键要定义在‘多’的一方!
外键删除操作
如果一个模型使用了外键,那么对方在哪个模型被删除后,该进行什么操作。可以用过on_delete来指定
- CASCADE 级联操作。外键对应的表删除了,这条记录也会被删除。
- PROTECT 受保护。只要这个数据有外键数据引用,就不能删除外键的哪个数据。
- SET_NULL 设置为空。 可以被删除,设置外键的表的引用索引会设置为null
- SET_DEFAULT 设置默认
- SET() 如果外键那个数据被删除了,获取SET()函数的值来作为外键的值
- DO_NOTHING 不采取任何行为
准备工作
打开MySQL查看数据支持引擎
mysql> show engines;
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| Engine | Support | Comment | Transactions | XA | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| InnoDB | YES | Supports transactions, row-level locking, and foreign keys | YES | YES | YES |
| MRG_MYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO |
| MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO |
| BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears) | NO | NO | NO |
| MyISAM | DEFAULT | MyISAM storage engine | NO | NO | NO |
| CSV | YES | CSV storage engine | NO | NO | NO |
| ARCHIVE | YES | Archive storage engine | NO | NO | NO |
| PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO |
| FEDERATED | NO | Federated MySQL storage engine | NULL | NULL | NULL |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
9 rows in set (0.00 sec)
设置InnoDB为默认引擎:在配置文件my.ini中的 mysqld 下面加入default-storage-engine=INNODB
案例
比如有一个用户表和一个文章表两个模型。一个用户可以发布多个文章,一个文章只能有一个作者。通过外键来引用。相关代码
from django.db import models
# Create your models here.
class User(models.Model):
username = models.CharField(max_length=20, default='')
password = models.CharField(max_length=200, default='')
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
user = models.ForeignKey("User", on_delete=models.CASCADE)
自动生成外键user_id
视图
from django.http import HttpResponse
from .models import User, Article
# 添加操作
def add(request):
user = User(username="张三", password="123456")
user.save() # 保存
article = Article(title='文章标题1', content='文章噢!')
article.user = user
article.save()
return HttpResponse("Add Ok!")
# 删除操作
def delete(request):
user = User(pk=1)
user.delete()
return HttpResponse("Delete")
当我们删除了用户ID为1的记录,那么查看文章表会发现对应的ID文章也会被删除掉。
当我们操作用户表,也会影响文章表。
案例
class User(models.Model):
username = models.CharField(max_length=20, default='')
password = models.CharField(max_length=200, default='')
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
user = models.ForeignKey("User", on_delete=models.CASCADE)
在写文章的时候指定作者。
user = User(username="张三", password="123456")
user.save()
article = Article(title='文章标题1', content='文章噢!')
article.user = user # 指定文章的作者是上面保存的User
article.save()
查询时候
user本来没有article_set的字段的但是Django为我们自动生成了。
def search(request):
# 获取某个用户下所有文章
user = User.objects.first() # 获取一个用户
print(user)
artlist = user.article_set.all() # 获取用户的文章
print(artlist)
return HttpResponse("Search")
>>>
User: 李四
<QuerySet [<Article: Article: 文章标题1>, <Article: Article: 文章标题3>]>
添加操作
那么我们如何将某个文章写入到用户呢?
def add(request):
user = User.objects.first() # 获取一个用户
print(user)
article = Article(title="bbb", content="asdasd5")
user.article_set.add(article, bulk=False) # 用户添加一个文章
# bulk = False 表示上面的article可以不用保存就可以存储。Django已经为我们做了操作
return HttpResponse("Add Success")
多对多的关系
ManyToManyField
案例
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
tags = models.ManyToManyField("Tag", related_name="tags")
def __str__(self):
return "Article: {}".format(self.title)
class Tag(models.Model):
name = models.CharField(max_length=50)
在数据库层面,实际为多对多的关系建立了一个中间表。这个表分别定义了两个外键。引用两个表的主键。
获取操作
# 获取某个文章下的所有标签
article = Article.objects.first()
taglist = article.tags.all()
print(taglist)
保存操作
article = Article.objects.first()
tag = Tag('热门文章')
tag.save()
article.tag_set.add(tag)
API
related_name
用于关联对象反向引用模型的名称。以前面车和工厂的例子解释,就是从工厂反向关联到车的关系名称。
通常情况下,这个参数我们可以不设置,Django会默认以模型的小写加上_set
作为反向关联名,比如对于工厂就是car_set
,如果你觉得car_set
还不够直观
related_query_name
反向关联查询名。用于从目标模型反向过滤模型对象的名称。
技巧
Django外键查询及@property在model中的妙用
有两张表,Person表是主表,Car是字表,Car表外键至Person表
class Person(models.Model):
age = models.IntegerField()
name = models.CharField(max_length=100)
@property
def all_cars(self):
return self.cars.all()
@property
def message(self):
return '{}-{}'.format(self.name, self.age)
class Meta:
db_table = 'person'
def __str__(self):
return "<Person: {}>".format(self.name)
class Car(models.Model):
owner = models.ForeignKey(Person, on_delete=models.CASCADE, related_name='cars')
name = models.CharField(max_length=64)
price = models.FloatField()
class Meta:
db_table = 'car'
# 子表查询主表
In [3]: car = Car.objects.get(id=1)
In [4]: car
Out[4]: <Car: Car object (1)>
In [6]: print(car.owner.name)
Luck
# 主表查子表
# 方式一
# Django默认每个主表对象都有一个外键的属性
# 可以通过它来查询所有属于主表的子表信息
# 查询方式:主表.子表_set()
# 返回值为一个queryset对象
luck.car_set().all()
# 方式二:
# 通过在外键中设置related_name属性值既可
luck.cars.all()
# 方式三:
# 通过@property装饰器在model中预定义方法实现
luck = Person.objects.filter(id=1).first()
print(luck.all_cars)
其他
引用其他APP下的模型
如果想要引用另外APP下的模型。
格式
app.模型的名字
引用自身模型
如果模型的外键引用自己的这个模型。那么to参数设置为self。或者是这个模型的名字。在评论模型中,一般评论都可以进行二级评论,既可以针对另一个评论进行评论。那么可以定义模型的时候就需要外键来引用自身。