文章目录
外键和表的关系
在MySQL中,表有两种引擎,一种是InnoDB,另外一种是myisam。如果使用的是InnoDB引擎,是支持外键约束的。外键的存在使得ORM框架在处理表关系的时候异常的强大。因此这里我们首先来介绍下外键在Django中使用。
class ForeignKey(ForeignObject):
def __init__(self, to, on_delete):
类定义为class ForeignKey(to,on_delete)
。第一个参数是引用的是哪个模型,第二个参数是在使用外键引用的模型数据被删除了,这个字段该如何处理,比如有CASCADE 、 SET_NULL
。
注意
-
一个Category模型可以有对应多个模型,为什么?因为Category对应过来是分类。
-
一个Article只能有一个Category,并且通过外键进行引用。
-
使用ForeignKey来定义模型之间的关系。
-
ForeignKey是由引用的模型来影响当前模型。
from django.db import models # category : 分类的意思 # 该类为分类 class Category(models.Model): name = models.CharField(max_length=100) # 该类为文章类 class Article(models.Model): title = models.CharField(max_length=100) content = models.TextField() # Foreign key 对应的是映射主键 # ForeignKey对应着第一个参数为要引用的模型,第二个参数为使用外键引用的模型被删除之后的字段处理 # CASCADE 为级联删除 category = models.ForeignKey('Category', on_delete=models.CASCADE)
from django.shortcuts import render
from .models import Article, Category
from django.http import HttpResponse
def index(request):
category = Category(name='Python')
# 先要保存好引用模型的实例
category.save()
article = Article(title='Python数据分析', content='主要学习的内容有Numpy、Pandas、Matplotlib、Seaborn')
# 这一步就完成了外键与表之间的映射
# 让对应Article中的category这个外键关系等于对应实例化模型
article.category = category
article.save()
return HttpResponse('首页')
当使用另外APP外键时
想要引用另外一个app的模型,那么应该是传递to参数的时候,使用app.model_name
进行指定。
使用另外APP下的外键:对应的APP.对应的模型
user/models.py
class UserName(models.Model):
username = models.CharField(max_length=100)
front/models.py
# 对应的文章
class Article(models.Model):
title = models.CharField(max_length=20)
# TextField 可以不需要指定长度
content = models.TextField()
# ForeignKey默认传递两个参数。
# to-->对应的模型的名字
# on_delete --> 对应模型的外键删除操作
# 使用对应的外键 ForeignKey
category = models.ForeignKey('Category', on_delete=models.CASCADE)
# 当想使用user下的models作为外键时
author = models.ForeignKey('user.UserName', on_delete=models.CASCADE, null=True)
front/views.py
from django.shortcuts import render
from django.http import HttpResponse
from .models import Article, Category
from user.models import UserName
def index(request):
article = Article(title='Python3反爬虫原理与绕过实战', content='解决反爬虫原理')
article.category = Category(pk=1)
article.author = UserName(pk=1)
article.save()
return HttpResponse('这是首页')
引用自身做为外键
如果模型的外键引用的是本省自己这个模型,那么to参数可以为self
,或者是这个模型的名字。在论坛开发中,一般评论都可以进行二级评论,即可以针对另外一个评论进行评论,那么在定义模型的时候就需要使用外键来引用自身。
# 该模型为引用自身模型来进行设置主键
# Comment : 评论
class Comment(models.Model):
content = models.TextField()
origin_comment = models.ForeignKey('self', on_delete=models.CASCADE, null=True)
外键操作
-
CASCADE
: 级联操作。如果外键对应的那条数据被删除了,那么这条数据也会被删除# CASCADE : 当外键被删除,字段也会跟着删除 category = models.ForeignKey('Category', on_delete=models.CASCADE)
-
PROTECT
: 受保护。即只要这条数据引用了外键的那条数据,那么就不能删除外键的那条数据。-
# PROTECT : 当有字段引用外键时,该外键是是不能删除的,受保护的情况 category = models.ForeignKey('Category', on_delete=models.PROTECT
-
-
SET_NULL
: 设置为空。如果外键的那条数据被删除了,那么在本条数据上就将这个字段设置为空。如果设置这个选项,前提是要指定这个字段可以为空。-
# SET_NULL : 前提的情况是该外键字段可以设置为空的时候,才能进行操作 category = models.ForeignKey('Category', on_delete=models.SET_NULL, null=True)
-
-
SET_DEFAULT
: 设置默认值。如果外键的那条数据被删除了,那么本条数据上就将这个字段设置默认值。如果设置这个选项,前提是要指定这个字段一个默认值。-
# SET_DEFAULT : 前提的情况是该外键字段可以设置默认值,才能进行操作 category = models.ForeignKey('Category', on_delete=models.SET_DEFAULT, default=2)
-
-
SET()
: 如果外键的那条数据被删除了。那么将会获得SET
函数中的值来作为这个外键的值。SET
函数可以接受一个可以调用的对象(比如函数或者方法),如果是可以调用对象,那么会将这个对象调用后的结果作为值返回 -
DO_NOTHING
: 不采取任何行为。一切全看数据库级别的约束。
查询操作
查询是数据库操作中一个非常重要的技术。查询一般就是使用filter
、exclude
、get
三个方法来实现。我们可以在调用这些方法的时候传递不同的参数来实现查询需求。在ORM层面,这些查询条件都是使用field+__+condition
的方式来使用的。
数据源
get
- 使用get来查询数据的话,查询数据不存在的情况下,会报错。
- 返回的类型为
<class 'front.models.Article'>
article = Article.objects.get(pk__exact=1)
- 打印原生的SQL语句,通过
from django.db import connection
connection.queries
filter
- 使用filter来查询数据的话,当数据不存在返回
<QuerySet []>
- 返回的类型为
<class 'django.db.models.query.QuerySet'>
article = Article.objects.filter(pk__exact=1)
- 打印原生的SQL语句,通过
article.query
来实现
exact
使用精确的=进行查找,如果提供的是一个None,那么在SQL层面就是被解释为NULL
article = Article.objects.get(pk__exact=1)
print(article)
print(connection.queries)
#----------#
# 结果
Article{title:Python3网络爬虫, author:small-j, category:1}
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}, {'sql': 'SELECT `front_article`.`id`, `front_article`.`title`, `front_article`.`author`, `front_article`.`category_id` FROM `front_article` WHERE `front_article`.`id` = 1', 'time': '0.000'}, {'sql': 'SELECT `front_category`.`id`, `front_category`.`name` FROM `front_category` WHERE `front_category`.`id` = 1', 'time': '0.001'}]
article = Article.objects.filter(pk__exact=None)
print(article)
print(article.query)
#----------#
# 结果
<QuerySet []>
SELECT `front_article`.`id`, `front_article`.`title`, `front_article`.`author`, `front_article`.`category_id` FROM `front_article` WHERE `front_article`.`id` IS NULL
iexact
使用like进行查询
article = Article.objects.filter(pk__iexact=1)
print(article)
print(article.query)
#----------#
# 结果
<QuerySet [<Article: Article{title:Python3网络爬虫, author:small-j, category:1}>]>
SELECT `front_article`.`id`, `front_article`.`title`, `front_article`.`author`, `front_article`.`category_id` FROM `front_article` WHERE `front_article`.`id` LIKE 1
contains
大小写敏感
article = Article.objects.filter(title__contains='Python')
print(article)
print(article.query)
# ------- #
# 结果
<QuerySet [<Article: Article{title:Python3网络爬虫, author:small-j, category:1}>, <Article: Article{title:Python 数据分析, author:small-j, category:1}>]>
SELECT `front_article`.`id`, `front_article`.`title`, `front_article`.`author`, `front_article`.`category_id` FROM `front_article` WHERE `front_article`.`title` LIKE BINARY %Python%
icontaions
大小写不敏感
article = Article.objects.filter(title__icontains='Python')
print(article)
print(article.query)
# -------- #
# 结果
<QuerySet [<Article: Article{title:Python3网络爬虫, author:small-j, category:1}>, <Article: Article{title:Python 数据分析, author:small-j, category:1}>, <Article: Article{title:python Django, author:small-j, category:1}>]>
SELECT `front_article`.`id`, `front_article`.`title`, `front_article`.`author`, `front_article`.`category_id` FROM `front_article` WHERE `front_article`.`title` LIKE %Python%
in
提取那些给定的field
的值是否在给定的容器中。容器可以为list、tuple或者任何一个可以迭代的对象,包括QuerySet对象。
article = Article.objects.filter(pk__in=[1, 2, 3])
print(article)
print(article.query)
# ---------
<QuerySet [<Article: Article{title:Python3网络爬虫, author:small-j, category:1}>, <Article: Article{title:Java开发实战, author:small-j, category:2}>, <Article: Article{title:mysql数据库认知, author:small-j, category:3}>]>
SELECT `front_article`.`id`, `front_article`.`title`, `front_article`.`author`, `front_article`.`category_id` FROM `front_article` WHERE `front_article`.`id` IN (1, 2, 3)
# 查询文章为id[1,2]的对应的文章分类
# 反向引用
# article__id__in
category = Category.objects.filter(article__id__in=[1, 2])
print(category)
print(category.query)
return HttpResponse('删除成功')
#-------#
# 运行结果
<QuerySet [<Category: Category{name:Python}>, <Category: Category{name:java}>]>
SELECT `front_category`.`id`, `front_category`.`name` FROM `front_category` INNER JOIN `front_article` ON (`front_category`.`id` = `front_article`.`category_id`) WHERE `front_article`.`id` IN (1, 2)
比较运算
gt
某个field的值要大于给定的值
book = Book.objects.filter(price__gt=89)
print(book)
print(book.query)
#----------
<QuerySet [<Book: {title:代码大全(第2版), america_author:[美] 史蒂夫·迈克康奈尔 , author: 金戈、汤凌、陈硕、张菲 译、裘宗燕 审校 , press: 电子工业出版社 , press_time: 2006-3 , price:128.0}>]>
# 原生SQL语句
SELECT `book`.`id`, `book`.`title`, `book`.`america_author`, `book`.`author`, `book`.`press`, `book`.`press_time`, `book`.`price` FROM `book` WHERE `book`.`price` > 89.0
gte
某个field的值要大于等于给定的值
book = Book.objects.filter(price__gte=89)
print(book)
print(book.query)
# --------
<QuerySet [<Book: {title:程序员修炼之道(第2版), america_author:David Thomas、Andrew Hunt , author: 云风 , press: 电子工业出版社 , press_time: 2020-4-1 , price:89.0}>, <Book: {title:代码大全(第2版), america_author:[美] 史蒂夫·迈克康奈尔 , author: 金戈、汤凌、陈硕、张菲 译、裘宗燕 审校 , press: 电子工业出版社 , press_time: 2006-3 , price:128.0}>]>
# 原生SQL语句
SELECT `book`.`id`, `book`.`title`, `book`.`america_author`, `book`.`author`, `book`.`press`, `book`.`press_time`, `book`.`price` FROM `book` WHERE `book`.`price` >= 89.0
lt
某个field的值要小于给定的值
book = Book.objects.filter(price__lt=49)
#-----
<QuerySet [<Book: {title:计算机程序的构造和解释(原书第2版), america_author:Harold Abelson、Gerald Jay Sussman、Julie Sussman , author: 裘宗燕 , press: 机械工业出版社 , press_time: 2004-2 , price:45.0}>]>
# 原生SQL语句
SELECT `book`.`id`, `book`.`title`, `book`.`america_author`, `book`.`author`, `book`.`press`, `book`.`press_time`, `book`.`price` FROM `book` WHERE `book`.`price` < 49.0
lte
某个field的值要小于等于给定的值
book = Book.objects.filter(price__lte=49)
# -----
<QuerySet [<Book: {title:黑客与画家, america_author:[美] Paul Graham , author: 阮一峰 , press: 人民邮电出版社 , press_time: 2011-4 , price:49.0}>, <Book: {title:计算机程序的构造和解释(原书第2版), america_author:Harold Abelson、Gerald Jay Sussman、Julie Sussman , author: 裘宗燕 , press: 机械工业出版社 , press_time: 2004-2 , price:45.0}>]>
# 原生SQL语句
SELECT `book`.`id`, `book`.`title`, `book`.`america_author`, `book`.`author`, `book`.`press`, `book`.`press_time`, `book`.`price` FROM `book` WHERE `book`.`price` <= 49.0
range
判断某个字段在不在区域间
start_time = date(year=2020, month=7, day=3)
end_time = date(year=2020, month=7, day=4)
book = Book.objects.filter(now_time__range=(start_time, end_time))
print(book)
print(book.query)
# ------
<QuerySet [<Book: {title:黑客与画家, america_author:[美] Paul Graham , author: 阮一峰 , press: 人民邮电出版社 , press_time: 2011-4 , price:49.0, now_time:2020-07-04}>, <Book: {title:程序员修炼之道(第2版), america_author:David Thomas、Andrew Hunt , author: 云风 , press: 电子工业出版社 , press_time: 2020-4-1 , price:89.0, now_time:2020-07-03}>, <Book: {title:计算机程序的构造和解释(原书第2版), america_author:Harold Abelson、Gerald Jay Sussman、Julie Sussman , author: 裘宗燕 , press: 机械工业出版社 , press_time: 2004-2 , price:45.0, now_time:2020-07-03}>]>
# 原生SQL语句
SELECT `book`.`id`, `book`.`title`, `book`.`america_author`, `book`.`author`, `book`.`press`, `book`.`press_time`, `book`.`price`, `book`.`now_time` FROM `book` WHERE `book`.`now_time` BETWEEN 2020-07-03 AND 2020-07-04
date
针对某些date或者datetime类型的字段。可以指定date的范围。并且这个时间过滤,还可以以使用链式调用
book = Book.objects.filter(create_time__date=datetime(year=2020, month=7, day=5))
#-------
<QuerySet [<Book: {title:Python深度学习, america_author:[美] 弗朗索瓦•肖莱 , author:张亮 , press: 人民邮电出版社 , price:119.0, create_time:2020-07-05 13:44:38}>]>
# 原生SQL语句
SELECT `book`.`id`, `book`.`title`, `book`.`america_author`, `book`.`author`, `book`.`press`, `book`.`price`, `book`.`create_time` FROM `book` WHERE DATE(`book`.`create_time`) = 2020-07-05
year
根据年份进行查找
book = Book.objects.filter(create_time__year=2020)
# -------
<QuerySet [<Book: {title:利用Python进行数据分析, america_author:Wes McKinney , author: 唐学韬 , press: 机械工业出版社 , price:89.0, create_time:2020-07-03 13:46:21}>]>
#
SELECT `book`.`id`, `book`.`title`, `book`.`america_author`, `book`.`author`, `book`.`press`, `book`.`price`, `book`.`create_time` FROM `book` WHERE `book`.`create_time` BETWEEN 2020-01-01 00:00:00 AND 2020-12-31 23:59:59.999999
# 链式调用
book = Book.objects.filter(create_time__year__gt=2019)
# ---------
<QuerySet [<Book: {title:利用Python进行数据分析, america_author:Wes McKinney , author: 唐学韬 , press: 机械工业出版社 , price:89.0, create_time:2020-07-03 13:46:21}>]>
# 原生SQL
SELECT `book`.`id`, `book`.`title`, `book`.`america_author`, `book`.`author`, `book`.`press`, `book`.`price`, `book`.`create_time` FROM `book` WHERE `book`.`create_time` > 2019-12-31 23:59:59.999999
time
根据时间进行查找
start_time = time(hour=13, minute=44)
end_time = time(hour=13, minute=50)
book = Book.objects.filter(create_time__time__range=(start_time, end_time))
#---------
<QuerySet [<Book: {title:Python深度学习, america_author:[美] 弗朗索瓦•肖莱 , author:张亮 , press: 人民邮电出版社 , price:119.0, create_time:2019-06-05 13:44:38}>, <Book: {title:深入理解计算机系统(原书第3版), america_author: Randal E.Bryant、David O'Hallaron , author:龚奕利、贺莲 , press: 机械工业出版社 , price:139.0, create_time:2019-06-04 13:49:41}>]>
# 原生SQL
SELECT `book`.`id`, `book`.`title`, `book`.`america_author`, `book`.`author`, `book`.`press`, `book`.`price`, `book`.`create_time` FROM `book` WHERE TIME(`book`.`create_time`) BETWEEN 13:44:00 AND 13:50:00
聚合函数的使用
Avg
求平均值。
from django.shortcuts import render
from django.http import HttpResponse
from .models import Book
from django.db import connection
# 导入聚合函数
from django.db.models import Avg
def index(request):
# 求书的价格的所以平均值
result = Book.objects.aggregate(Avg('price'))
print(result)
print(connection.queries)
return HttpResponse('前端页面')
# ----------------------
# 'proce__avg':这名字的由来,对应的APP的名字加上__avg
{'price__avg': 97.25}
# 原生SQL
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}, {'sql': 'SELECT AVG(`book`.`price`) AS `price__avg` FROM `book`', 'time': '0.001'}]
当想指定其他名字的时候,可以这么做
result = Book.objects.aggregate(pirce=Avg('price'))
聚合函数也支持查询条件的链式调用
# 求id价格大于2的平均值
result = Book.objects.filter(id__gt=2).aggregate(price=Avg('price'))
print(result)
print(connection.queries)
#-----------
{'price': 97.0}
# SQL
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}, {'sql': 'SELECT AVG(`book`.`price`) AS `price` FROM `book` WHERE `book`.`id` > 2', 'time': '0.001'}]
Count
获取指定的对象的个数
def count_views(request):
# Count:为数据的个数
result = Book.objects.aggregate(count=Count('id'))
print(result)
print(connection.queries)
return HttpResponse('聚合函数')
Max和Min
获取指定对象的最大值和最小值
result = Author.objects.aggregate(min=Min('age'))
result = Author.objects.aggregate(max=Max('age'))
aggregate和annotate的区别
- aggregate : 返回使用聚合函数的字段和值。
- annotate : 在原来模型字段的基础之上添加一个使用了聚合函数的字段,并且在使用聚合函数的时候,会使用当前这个模型的主键进行分组(group by)。
- aggregate返回的类型是一个字典类型,而annotate返回的类型为QuerySet类型。
def count_views(request):
for result in results:
print(result.name, result.avg)
print(connection.queries)
return HttpResponse('聚合函数')
F表达式和Q表达式
F表达式是用来优化ORM操作数据库的。
def F_data(request):
# 给所有的图书升价
# F表达式使用的是update
result = Book.objects.update(price=F('price')+10)
print(result)
return HttpResponse('F表达式')
Q表达式是如果想要实现所有价格高于100元,并且评分达到4.8以上评分的图书
books = Book.objects.filter(price__gt=100, rating__gt=4.8)
以上这个案例是一个并集查询,可以简单的通过传递多个条件进去来实现。但是如果想要实现一些复杂的查询语句,比如要查询所有价格高于100元,或者是评分低于4.8分的图书。那就没有办法通过传递多个条件进去实现了。这时候就需要使用Q表达式来实现了。
def Q_data(request):
# 价格高于100元 或者评分达到4.8以上的评分
books = Book.objects.filter(Q(price__gt=100) | Q(rating__gt=4.8))
for book in books:
print(book.name, book.price, book.rating)
return HttpResponse('Q表示式')
以上是进行或运算,当然还可以进行其他的运算,比如有&和~(非)等。
# 获取价格大于100,并且书名中包含"传"的图书
books = Book.objects.filter(Q(price__gte=100)&Q(name__contains="传"))
# 获取书名包含"传",但是id不等于3的图书
books = Book.objects.filter(Q(name__contains='传') & ~Q(id=3))