Django----外键和表&查询操作&聚合函数&F表达式&Q表达式

外键和表的关系

在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: 不采取任何行为。一切全看数据库级别的约束。

查询操作

查询是数据库操作中一个非常重要的技术。查询一般就是使用filterexcludeget三个方法来实现。我们可以在调用这些方法的时候传递不同的参数来实现查询需求。在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))

猜你喜欢

转载自blog.csdn.net/qq_37662827/article/details/107109666