草稿四

● 外键的删除操作---on_delete参数指定

<1> models.CASCADE---级联删除
外键表的字段被删除后,原始表的整行数据也会被删除

models

class Article(models.Model):
title=models.CharField(max_length=150)
content=models.TextField()
category=models.ForeignKey('Category',on_delete=models.CASCADE)
author=models.ForeignKey('front.FrontUser',on_delete=models.CASCADE,null=True)

article.views

def delete_view(request):
#category=Category.objects.first()
#category.delete() # 无需再调用category.save()
category=Category.objects.get(pk=2)
category.delete() # 外键表删除后,原始表的整行数据也会被删除
return HttpResponse('delete success!!!')

<2> models.PROTECT---保护
删除外键的数据后,就会提示报错保护,导致删除操作无法被执行
注意:此时外键的数据依然存在的,没有被删除

models

class Article(models.Model):
title=models.CharField(max_length=150)
content=models.TextField()
# 指定on_delete=models.PROTECT
category=models.ForeignKey('Category',on_delete=models.PROTECT)
author=models.ForeignKey('front.FrontUser',on_delete=models.CASCADE,null=True)

<3> models.SET_NULL---外键表的一行数据被删除以后,原始表的关联字段被替换成'null'
注意,原始表的数据依然存在(不会被删除)

class Article(models.Model):
title=models.CharField(max_length=150)
content=models.TextField()
category=models.ForeignKey('Category',on_delete=models.SET_NULL,null=True)
author=models.ForeignKey('front.FrontUser',on_delete=models.CASCADE,null=True)

<4> models.SET_DEFAULT---外键表的一行数据被删除以后,原始表的关联字段被替换成
另外一个关键字参数default传的值

class Article(models.Model):
title=models.CharField(max_length=150)
content=models.TextField()
# default=Category.objects.get(pk=5) 这么写会报'序列化'错误,不清楚原因
category=models.ForeignKey('Category',
on_delete=models.SET_DEFAULT,null=True,default=5)
author=models.ForeignKey('front.FrontUser',on_delete=models.CASCADE,null=True)

<5> models.SET()---外键被删除以后,原始表的关联字段被替换成'set()传的值'
注意,关联字段该行的其他数据依然存在(不会被删除)

from django.db import models

定义一个返回默认值的函数

def default_category():
return Category.objects.get(pk=5)

class Category(models.Model):
name=models.CharField(max_length=100)

class Article(models.Model):
title=models.CharField(max_length=150)
content=models.TextField()
# models.SET(5) 这么写也可以
category=models.ForeignKey('Category',
on_delete=models.SET(default_category),null=True) # 注意这里的值是函数对象,而不是去调用函数方法
author=models.ForeignKey('front.FrontUser',on_delete=models.CASCADE,null=True)

class Comment(models.Model):
content=models.CharField(max_length=200)
orgin_comment=models.ForeignKey('self',on_delete=models.CASCADE)

● 表和表之间的关系,通过'外键'关联,那么,表和表之间的关系无非三种
'一对一','多对多','一对多(多对一)'

• 一对多(多对一)---通过ForeignKey属性实现
示例:

article.models

from django.db import models

def default_category():
return Category.objects.get(pk=5)

class Category(models.Model):
name=models.CharField(max_length=100)

Article模型有4个字段,其中有两个字段是外键

class Article(models.Model):
title=models.CharField(max_length=150)
content=models.TextField()
category=models.ForeignKey('Category',
on_delete=models.SET(default_category),null=True)
author=models.ForeignKey('front.FrontUser',on_delete=models.CASCADE,null=True)

article.views

def one_to_many_view(request):
# 先实例化一个对象
article=Article(title='A new day',content='Really really really')
# 获取各自的字段
category=Category.objects.first() # category存储的是Category模型的第一行所有数据
author=FrontUser.objects.first()
# 赋值并保存(注意保存)
article.category=category
article.author=author # author外键关联外键表的一整行数据,而不仅仅是其中的一个字段
article.save()
return HttpResponse('Successful!!!')

此时,若想获取一个category目录下面的所有'article'该怎么处理?
答案是利用动态生成的属性---'article_set' # 或者利用'related_name'参数
article_set属性类似objects,也有first(),all()等等方法,它返回的也是QuerySet对象
示例:

def one_to_many_view(request):

category=Category.objects.first()
article=category.article_set.all() # 也有这样的方法: article_set.first()
for article in articles:
    print(article) #<Article:双英电影,Article:这是中英双字幕的电脑> <Article:A new day,Article:Really really really>
return HttpResponse('Successful!!!')

注意,'article_set'这个动态属性名称是可以更改的,怎么改呢---'related_name'(也叫'反转')
示例:

class Article(models.Model):
title=models.CharField(max_length=150)
content=models.TextField()
category=models.ForeignKey('Category',
on_delete=models.SET(default_category),related_name='articles',null=True) # 添加related_name参数
author=models.ForeignKey('front.FrontUser',on_delete=models.CASCADE,null=True)

def one_to_many_view(request):

category=Category.objects.first()
articles=category.articles.all() # 不再使用article_set属性
for article in articles:
    print(article)
return HttpResponse('Successful!!!')

• article.add()方法---分类下把文章添加进去
示例:

def one_to_many_view(request):
# 新增一条记录,但category没有赋值
article=Article(title='From now...',content='precious your time')
article.author=FrontUser.objects.first() # first()和get()的类型一样,都是模型对象,区别于QuerySet对象
article.save()
# 获取category并把文章添加进来
category = Category.objects.first()
category.articles.add(article) # 注意:在多对多关系中,add()会自动保存,无需再调用save()方法
category.save()
return HttpResponse('Successful!!!')

• article.add(bulk=False)---可以省略save()语句
示例:

def one_to_many_view(request):
article=Article(title='another test...',content='just just a ...')
article.author=FrontUser.objects.first()

category = Category.objects.first()
# bulk这个参数,在'一对多'的关系中才有
category.articles.add(article,bulk=False)# 无需article.save(),category.save()

return HttpResponse('Successful!!!')

bulk源码

    def add(self, *objs, bulk=True): # 默认为True
        ......

        if bulk:
         ......
        else: # bulk=False
            with transaction.atomic(using=db, savepoint=False):
                for obj in objs:# 遍历所有的对象,实现批量保存
                    check_and_update_obj(obj)
                    obj.save() # 执行save()方法
    add.alters_data = True

• 一对一关系---'models.OneToOneField':实质也是外键
比如原始表定义:telephone=models.OneToOneField('TelephoneNumber',on_delete=models.CASCADE,null=True)
那就表明,'telephone'这个外键字段的'值'是不能重复的;但是...外键表的字段值不受约束,是可以重复的...
示例:

front.models

from django.db import models

class FrontUser(models.Model):
front_user=models.CharField(max_length=50)

后面添加的拓展类

class FrontUserExtension(models.Model):
school=models.CharField(max_length=100)
user=models.OneToOneField('FrontUser',on_delete=models.CASCADE)# 使用OneToOneField属性

article.views

def one_to_one_view(request):

user=FrontUser.objects.first()
user_extension=FrontUserExtension(school='zhiliao')
user_extension.user=user
user_extension.save()
return HttpResponse('Successful!!!')

刷新一下数据库,可以到效果,其实质也是外键
此时若实例化另一个对象,依旧赋值user,那么就会报错,因为是一对一关系

注意,上述示例如果修改一处,结论就变了

'''
def index(request):
author=Author.objects.last()
telephone=TelephoneNumber.objects.create(telephone=1234567891) # 这么写,等于每次都重新建立一条数据,所以不会报错
author.telephone=telephone
author.save()
return HttpResponse('index')
'''

• 反向查询---区别于'一对多'关系,没有'model_set'这个属性
而是直接'model'属性
示例:

models

from django.db import models

class Student(models.Model):
name=models.CharField(max_length=100)

class School(models.Model):
grade=models.CharField(max_length=100)
user=models.OneToOneField('Student',on_delete=models.CASCADE)

views

通过外键表去查询原始表的数据

def index3(request):
student=Student.objects.get(pk=3)
result=student.school.grade # 使用school属性去访问原始表字段,返回初三
print(result)
return HttpResponse('index3')

• 一对一的related_name用法
示例:

models

class FrontUserExtension(models.Model):
school=models.CharField(max_length=100)
# 添加related_name参数
user=models.OneToOneField('FrontUser',on_delete=models.CASCADE,related_name='userextension')

views

def one_to_one_view(request):

# extension=FrontUserExtension.objects.first()
# print(extension.user) # 直接关联外键
user=FrontUser.objects.first()
print(user.userextension) # 外键的字段怎么转回去?---related_name参数
return HttpResponse('Successful!!!')

review 示例:

models

class Student(models.Model):
name=models.CharField(max_length=100)

class School(models.Model):
grade=models.CharField(max_length=100)
user=models.OneToOneField('Student',on_delete=models.CASCADE,
related_name='shool_user')

views

def index3(request):
student=Student.objects.get(pk=3)
result=student.shool_user.grade # 通过school_user这个属性去访问原始表字段
print(result)
return HttpResponse('index3')


小结:关于'一对一'和'一对多(多对一)'的反向查询,最好指定'related_name'参数
避免造成混乱,也避免记忆

● 外键多对多关系

以'文章'和'标签'示例,用来处理'多对多'关系的,实质上是有3张表
'文章表','中间表'(定义两个外键,来引用两张表的主键),'标签表'
示例:

models

class Tag(models.Model):
label=models.CharField(max_length=50) # 定义一个char字段
# 注意,这个属性没有 on_delete 参数
article=models.ManyToManyField('Article',related_name='tag') # 定义一个manytomany字段

class Article(models.Model):
title=models.CharField(max_length=150)
content=models.TextField()
category=models.ForeignKey('Category',
on_delete=models.SET(default_category),related_name='articles',null=True)
author=models.ForeignKey('front.FrontUser',on_delete=models.CASCADE,null=True)

def __str__(self):
    message='<Article:{0},Article:{1}>'.format(self.title,self.content)
    return message

迁移并写入数据库后,查看数据表
在'article_tag表中',有'id'和'label'列,查看这个表的外键,啥都没?在哪呢...
答案就是'中间表---article_tag_article',表里有3个字段'id','tag_id','article_id'
其中,'tag_id'和'article_id'是外键,分别关联两张表的主键字段

views

def many_to_many_view(request):

article=Article.objects.first()
tag=Tag(label='热门文章')
tag.save() # 记得保存
article.tag.add(tag) # 使用add()方法后,无需save()操作了
return HttpResponse('Successful...')

那么,是否可以参考之前的操作,使用bulk(一对多/多对一)参数呢?示例:

def many_to_many_view(request):
article=Article.objects.first()
tag=Tag(label='热门文章')
###tag.save()
article.tag.add(tag,bulk=False) # 添加bulk参数
return HttpResponse('Successful...')

结果报错了...### add() got an unexpected keyword argument 'bulk'
说明多对多关系,是没有这个参数的.

我们再给一个标签:

def many_to_many_view(request):
article=Article.objects.first()
tag=Tag(label='冷门文章')
tag.save()
article.tag.add(tag)
return HttpResponse('Successful...')

再次查看中间表,可以看到,一篇文章,确实可以有两个label,那么,反过来呢?试试...

def many_to_many_view(request):
# 对于id=1的tag,插入两条文章
tag=Tag.objects.get(pk=1)
#article=Article.objects.get(pk=4)
article=Article.objects.get(pk=5)
tag.article.add(article)
return HttpResponse('Successful...')

查看中间表,成功实现效果

获取文章下面所有label示例:

def many_to_many_view(request):

article=Article.objects.get(pk=3)
tags=article.tag.all()
for tag in tags:
    print(tag)
return HttpResponse('Successful...')

小结:外键不管定义的是哪种关系,'related_name'这个参数最好预先定义好,避免使用'ModelName_set'或者'ModelName'出现混乱


DB 查询操作

<1>pycharm配置数据库错误

[08001] Could not create connection to database server. Attempted reconnect 3 times. Giving up.

解决办法:时区问题导致

将URL中内容更改成如下形式:? 前面的'youdatabasename'替换成你自己的数据库名称

jdbc:mysql://localhost:3306/youdatabasename?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCo

<2> 查询的方法---'filter','exclude','get',三个方法,通过传递不同的参数来获取查询结果
参数的形式---field+'__'+condition # article=Article.objects.filter(title__contains='51cto')

下来,针对'condition'进行说明

A. exact(相当于'等于')
示例:

def index(request):
# 这里的'等于',不区分大小写
#article=Article.objects.filter(title__exact='51CTO')
article=Article.objects.filter(title__exact='51cto') # 精确匹配,不区分大小写(在Linux底下,就区分大小写啦...)
print(article) # <QuerySet [<Article: <Model:Article,Title:51cto>>]>
print(article.query) # SELECT article.id, article.title, article.content FROM article WHERE article.title = 51cto
return HttpResponse('data update successful!')

review示例

def index2(request):
articles=Article.objects.filter(title__exact='JS Article')
print(articles) # <QuerySet [<Article: Article object (2)>]>
print(articles[0].title) # JS Article 这种写法也是可行的,而且执行的SQL语句也会比较少,效率高
for article in articles:
print(article.title) # JS Article
return HttpResponse('index2')

注意:QuerySet对象既能支持遍历,也可以使用'[索引]'的形式,去提取里面的对象

若值设置为None,则对应db的null

article=Article.objects.filter(title__exact=None)

SELECT article.id, article.title, article.content FROM article WHERE article.title IS NULL

• iexact---windows系统几乎与 exact 没有区别,相当于db的'like'语句
示例:

def index(request):
article=Article.objects.filter(title__iexact='51CTO')
#article=Article.objects.filter(title__exact=None)
print(article) # <QuerySet [<Article: <Model:Article,Title:51cto>>]>
print(article.query) # SELECT article.id, article.title, article.content FROM article WHERE article.title LIKE 51CTO
return HttpResponse('data update successful!')

注意:article.query这个属性,必须是QuerySet对象才有的,若这里使用get()方法,返回的是模型对象,就没有query这个属性了
也就是说,无法获取sql语句啦---<class 'front.models.Article'>
示例:

def index(request):
article=Article.objects.get(title__iexact='51CTO')
print(article)
print(article.query) # 'Article' object has no attribute 'query'
return HttpResponse('data update successful!')

B.contains---某个字段是否包含某个数据(大小写敏感),模糊查找
示例:

def index(request):

article=Article.objects.filter(title__contains='51cto')
# 改成下面的形式,就找不到了,因为查询条件是大写
# article=Article.objects.filter(title__contains='CTO')
print(article) # <QuerySet [<Article: <Model:Article,Title:51cto>>]>
# 注意sql语句末尾的'%51cto%',前后的'%'表示任意字符,意味着这是一个模糊查找
print(article.query) # SELECT `article`.`id`, `article`.`title`, `article`.`content` FROM `article` WHERE `article`.`title` LIKE BINARY %51cto%

return HttpResponse('data update successful!')

• icontains---大小写不敏感
示例:

def index(request):

article=Article.objects.filter(title__icontains='CTO')
print(article)# <QuerySet [<Article: <Model:Article,Title:51cto>>]>
print(article.query)# SELECT `article`.`id`, `article`.`title`, `article`.`content` FROM `article` WHERE `article`.`title` LIKE %CTO%
return HttpResponse('data update successful!')

● in---提取的field值是否在指定的容器中(可迭代对象,例如list,tuple,query_set对象)
示例:

def index(request):

articles=Article.objects.filter(id__in=[1,2,3]) # 这里使用id字段查询,pycharm不会自动补齐(title,content字段会自动补齐)
for article in articles:
    print(article) # 获取了3条结果:<Model:Article,Title:51cto> <Model:A:oschina> <Model:Article,Title:hao123>
print(articles.query) # SELECT `article`.`id`, `article`.`title`, `article`.`contenrticle,Titlet` FROM `article` WHERE `article`.`id` IN (1, 2, 3)
return HttpResponse('data update successful!')

• 跨表的查询操作

models

from django.db import models

class Categories(models.Model):

name=models.CharField(max_length=100)

def __str__(self):
    message='<Model:Categories,Name:{}>'.format(self.name)
    return message

class Article(models.Model):

title=models.CharField(max_length=100)
content=models.TextField()
category=models.ForeignKey('Categories',on_delete=models.CASCADE,
                           # 反向查询参数
                           related_query_name='article',null=True)
                           # 模型对象查询参数---related_name
                           # QuerySet对象查询参数---related_query_name

class Meta:
    db_table='article'

def __str__(self):
    message='<Model:Article,Title:{}>'.format(self.title)
    return message

现在往Categories模型插入一些数据,然后在view操作:

from django.shortcuts import render
from django.http import HttpResponse
from .models import Article,Categories

def index(request):

# model__field__condition

# 获取id为1,2,3的文章分类
# categories=Categories.objects.filter(article__id__in=[1,2,3]) 一样的效果
categories=Categories.objects.filter(article__in=[1,2,3]) # 默认就是使用主键查询
for category in categories:
    print(category) # 获取到3条数据
print(categories.query)
# SELECT `front_categories`.`id`, `front_categories`.`name` FROM `front_categories` INNER JOIN `article` ON (`front_categories`.`id` = `article`.`category_id`) WHERE `article`.`id` IN (1, 2, 3)

return HttpResponse('data update successful!')

review---比较'related_name'和'related_query_name'的区别,其实就是模型对象和'QuerySet对象'的区别

models

class Category(models.Model):
label=models.CharField(max_length=50)

class Article(models.Model):
title=models.CharField(max_length=50)
content=models.TextField()
author=models.ForeignKey('Author',on_delete=models.CASCADE,null=True)
category=models.ForeignKey('Category',on_delete=models.CASCADE,null=True,
related_name='article',related_query_name='query_article') # 分别设置两个参数名称

views

def index2(request):

categories=Category.objects.filter(query_article__in=[1,2,3])# QuerySet对象查询条件使用
for category in categories:
    print(category.label) # Hot Hot Ordinary

print('*'*40)
category1=Category.objects.first()# 获取模型对象
articles=category1.article.all() # 模型对象使用
for article in articles:
    print(article.title)
    '''
    How To Learn English
    JS Article
    Basketball
    '''


return HttpResponse('index2')

可以看到,简单的orm代码,就可以实现复杂的sql语句操作

• 所有标题中包含xxx的分类示例1:

def index(request):
# 正向查询
articles=Article.objects.filter(title__icontains='hao')
for article in articles:
print(article.category) # <Model:Categories,Name:最火>
return HttpResponse('data update successful!')

示例2---传入QuerySet对象:

def index(request):

# 反向查询
# 获取分类---标题包含xx的分类
articles=Article.objects.filter(title__icontains='hao')
categories=Categories.objects.filter(article__id__in=articles) # 传入QuerySet对象
for category in categories:
    print(category)
return HttpResponse('data update successful!')

注意:查询参数'article__in'包含非常多的查询语句,在pycharm里面有很详细的列出

• __gt(大于:greater),__gte(大于等于:greater equal),__lt(小于:lowter),__lte(小于等于:lowter equal)
示例:

def index(request):
# 查询id>2的所有文章
articles=Article.objects.filter(id__gt=2)
for article in articles:
print(article) # <Model:Article,Title:hao123> <Model:Article,Title:360>
print(articles.query) # SELECT article.id, article.title, article.content, article.category_id FROM article WHERE article.id > 2

return HttpResponse('data update successful!')

• startswith---字段的值以'xxx'开头,大小写敏感(istartswith:大小写不敏感)
示例:

def index(request):

articles=Article.objects.filter(title__startswith=360)
for article in articles:
    print(article) # <Model:Article,Title:360>
print(articles.query) # SELECT `article`.`id`, `article`.`title`, `article`.`content`, `article`.`category_id` FROM `article` WHERE `article`.`title` LIKE BINARY 360%
return HttpResponse('data update successful!')

注意sql语句末尾的'BINARY 360%'

endswith 用法类似,不再举例

● __range:查询某一个区间的值
示例:

models

class Article(models.Model):
title=models.CharField(max_length=100)
content=models.TextField()
category=models.ForeignKey('Categories',on_delete=models.CASCADE,
related_query_name='article',null=True)
create_time=models.DateTimeField(auto_now_add=True,null=True) # 添加时间字段

class Meta:
    db_table='article'

def __str__(self):
    message='<Model:Article,Title:{}>'.format(self.title)
    return message

views

from django.shortcuts import render
from django.http import HttpResponse
from .models import Article,Categories
from datetime import datetime # 导入时间模块

def index1(request):
# 获取8:30-9:30之间发布的所有文章
start_time=datetime(year=2019,month=12,day=4,hour=8,minute=30,second=0)
end_time=datetime(year=2019,month=12,day=4,hour=9,minute=30,second=0)
articles=Article.objects.filter(create_time__range=(start_time,end_time)) # 示例用法
print(articles.query) # SELECT article.id, article.title, article.content, article.category_id, article.create_time FROM article WHERE article.create_time BETWEEN 2019-12-04 00:30:00 AND 2019-12-04 01:30:00

for article in articles:
    print(article) # 获取了4条记录
return HttpResponse('Successful!')

虽然成功返回想要的结果,但是django有一个红色字体警告:

RuntimeWarning: DateTimeField Article.create_time received a naive datetime (2019-12-04 09:30:00) while time zone support is active.
RuntimeWarning)

我们之前说过,datetime获取的是一个'naive time',这里如果想转换成'aware time',需调用replace方法
但是,django提供另外的一个便捷的方法---make_aware()
示例:

from django.utils.timezone import make_aware # 这个模块还有make_naive方法

def index1(request):
start_time=make_aware(datetime(year=2019,month=12,day=4,hour=8,minute=30,second=0))# 调用的方式也相当简便
end_time=make_aware(datetime(year=2019,month=12,day=4,hour=9,minute=30,second=0))
articles=Article.objects.filter(create_time__range=(start_time,end_time))
print(articles.query)
for article in articles:
print(article)
return HttpResponse('Successful!')

查看 make_aware() 源码,其实调用的也是replace方法:

def make_aware(value, timezone=None, is_dst=None):
"""Make a naive datetime.datetime in a given time zone aware."""
if timezone is None:
timezone = get_current_timezone()
if hasattr(timezone, 'localize'):
# This method is available for pytz time zones.
return timezone.localize(value, is_dst=is_dst)
else:
# Check that we won't overwrite the timezone of an aware datetime.
if is_aware(value):
raise ValueError(
"make_aware expects a naive datetime, got %s" % value)
# This may be wrong around DST changes!
return value.replace(tzinfo=timezone)

• __date查询---使用的前提,需先配置mysql时区文件
示例:

def index1(request):

articles=Article.objects.filter(create_time__date=datetime(year=2019, # 使用__date查询
month=12,day=4))
print(articles.query)
for article in articles:
    print(article)
return HttpResponse('Successful!')

返回的结果为空...查看SQL语句:

SELECT article.id, article.title, article.content, article.category_id, article.create_time FROM article WHERE DATE(CONVERT_TZ(article.create_time, 'UTC', 'Asia/Shanghai')) = 2019-12-04

其中,'UTC','Asia/Shanghai'是SQL数据库不能识别的时区,怎么破?我们需要配制mysql的时区文件
下载地址:https://dev.mysql.com/downloads/timezones.html # timezone_2019c_posix_sql.zip - POSIX standard

替换以下路径的sql时区文件('''请先做好备份,以防万一''')
C:\ProgramData\MySQL\MySQL Server 5.5\data\mysql

然后重启mysql服务

不更改django任何代码,现在重新刷新网页,可以看到,查询到了我们想要的结果:

SELECT article.id, article.title, article.content, article.category_id, article.create_time FROM article WHERE DATE(CONVERT_TZ(article.create_time, 'UTC', 'Asia/Shanghai')) = 2019-12-04

<Model:Article,Title:51cto>
<Model:Article,Title:oschina>
<Model:Article,Title:hao123>
<Model:Article,Title:360>

• __year查询:查询年份
示例:

def index1(request):

articles=Article.objects.filter(create_time__year=2019) # SELECT `article`.`id`, `article`.`title`, `article`.`content`, `article`.`category_id`, `article`.`create_time` FROM `article` WHERE `article`.`create_time` BETWEEN 2018-12-31 16:00:00 AND 2019-12-31 15:59:59

print(articles.query)
for article in articles:
    print(article) # 获取到了4条记录
return HttpResponse('Successful!')

延伸示例 __year__gte:

def index1(request):

articles=Article.objects.filter(create_time__year__gte=2018) # 获取大于等于2018年份的数据
print(articles.query)
for article in articles:
    print(article)
return HttpResponse('Successful!')

'''SELECT article.id, article.title, article.content, article.category_id, article.create_time FROM article WHERE article.create_time >= 2017-12-31 16:00:00
[04/Dec/2019 09:40:39] "GET / HTTP/1.1" 200 11
<Model:Article,Title:51cto>
<Model:Article,Title:oschina>
<Model:Article,Title:hao123>
<Model:Article,Title:360>'''

• __day的用法和 __year 类似
示例:

只要是4号,不管是哪一个月份,都会返回结果

def index1(request):

articles=Article.objects.filter(create_time__day=4) # 成功获取
print(articles.query)
for article in articles:
    print(article)
return HttpResponse('Successful!')

• week_day:表示星期几,周日是1,周一到六是从:2-7
示例:

def index1(request):

articles=Article.objects.filter(create_time__week_day=4) # 获取周三发布的article数据
print(articles.query)
for article in articles:
    print(article)
return HttpResponse('Successful!')

• __time:根据时间查询,传入的time对象必须精确到秒,所以用区间(range)查询比较稳妥
其实精确到秒查询也是可以的,实战中很少会这么处理,一般都是用区间
示例:

def index1(request):

start_time=time(hour=8,minute=38,second=0)
end_time=time(hour=8,minute=38,second=59)
articles=Article.objects.filter(create_time__time__range=(start_time,end_time)) # 1分钟内的区间去获取结果
print(articles.query)
for article in articles:
    print(article)
return HttpResponse('Successful!')

'''
SELECT article.id, article.title, article.content, article.category_id, article.create_time FROM article WHERE TIME(CONVERT_TZ(article.create_time, 'UTC', 'Asia/Shanghai')) BETWEEN 08:38:00 AND 08:38:59
<Model:Article,Title:51cto>
<Model:Article,Title:oschina>
<Model:Article,Title:hao123>
<Model:Article,Title:360>

'''

• __isnull---判断某个字段是否为空

def index1(request):

articles=Article.objects.filter(create_time__isnull=True) # 获取到了一条结果,若这里为False获取到就是不为空的字段了
print(articles.query)
for article in articles:
    print(article)
return HttpResponse('Successful!')

'''
SELECT article.id, article.title, article.content, article.category_id, article.create_time FROM article WHERE article.create_time IS NULL

<Model:Article,Title:新浪>

'''

• __regex---正则查询,区分大小写(__iregex 不区分大小写)
使用这个参数,可以匹配出复杂的正则表达式.
示例:

def index1(request):

articles=Article.objects.filter(title__regex=r'^os') # 获取以os开头的title字段
print(articles.query)
for article in articles:
    print(article)
return HttpResponse('Successful!')

'''
SELECT article.id, article.title, article.content, article.category_id, article.create_time FROM article WHERE article.title REGEXP BINARY ^os
<Model:Article,Title:oschina>
'''

我们之前对字段是做了一些'查询'的操作,现在,我们对字段做'计算'操作

● 聚合函数(例如Avg,Count)的调用---aggregate():提取数据的一种方法
比如提取某个商品销售的数量,那么可以使用Count;如果想要知道商品销售的平均价格,那么可以使用Avg

聚合函数Count,Avg是不能直接调用了,得通过aggregate()方法调用
这个方法返回一个字典---例如使用'avg',默认'field_avg'的样式

'''
如果你用原生SQL,则可以使用聚合函数来提取数据。比如提取某个商品销售的数量,那么可以使用Count,
如果想要知道商品销售的平均价格,那么可以使用Avg。
聚合函数是通过aggregate方法来实现的,aggregate翻译就是'聚合',表示所有
'''

示例:

models

from django.db import models

class Author(models.Model):
name=models.CharField(max_length=100)
age=models.IntegerField()
email=models.EmailField()

class Meta:
    db_table='author'

class Publisher(models.Model):
name=models.CharField(max_length=300)

class Meta:
    db_table='publisher'

class Book(models.Model):
name=models.CharField(max_length=300)
pages=models.IntegerField()
price=models.FloatField()
rating=models.FloatField()
author=models.ForeignKey('Author',on_delete=models.CASCADE)
publisher=models.ForeignKey('Publisher',on_delete=models.CASCADE)

class Meta:
    db_table='book'

class BookOrder(models.Model):
book=models.ForeignKey('Book',on_delete=models.CASCADE)
price=models.FloatField()

class Meta:
    db_table='book_order'

views

from django.shortcuts import render
from .models import *
from django.http import HttpResponse
from django.db.models import Avg

def index(request):
result=Book.objects.aggregate(Avg('price')) # 这里的Avg('price')实质就是执行原生的SQL语句,所以可以这么写
print(result) # {'price__avg': 106.69999999999999}
return HttpResponse('Successful!')

注意: price__avg 结构是根据 field__avg 规则构成的如果想要修改默认的名字,那么可以将Avg赋值给一个关键字参数。示例代码如下:

views

def index(request):
result=Book.objects.aggregate(my_avg=Avg('price'))
print(result) # {'my_avg': 106.69999999999999}
return HttpResponse('Successful!')

注意,此时若想查询sql语句,使用以下代码是错误的:

print(result.query) # 因为result不是QuerySet对象

若想看SQL语句,可以这么处理:

from django.shortcuts import render
from .models import *
from django.http import HttpResponse
from django.db.models import Avg
from django.db import connection # 导入connection

def index(request):
result=Book.objects.aggregate(my_avg=Avg('price'))
print(result)
print(connection.queries) # 使用queries属性来获取SQL语句
return HttpResponse('Successful!')

结果:

{'my_avg': 106.69999999999999}

[{'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 my_avg FROM book', 'time': '0.001'}] # 列表里面的字典,最后一句就是执行的SQL语句

现在有一个这样的需求,获取每一本书销售的平均价格,是否这样处理:

from django.shortcuts import render
from .models import *
from django.http import HttpResponse
from django.db.models import Avg
from django.db import connection

def index(request):
# 获取每一本书销售的平均价格
# avg_result=Book.objects.aggregate(Avg('bookorder.price')) # 这种写法是错误的
result=Book.objects.aggregate(Avg('bookorder__price'))# 查询里面,使用双下划线的形式,而不是'.'号形式
print(result) # {'bookorder__price__avg': 96.66666666666667}
print(connection.queries)
'''
[{'time': '0.001', 'sql': 'SELECT @@SQL_AUTO_IS_NULL'}, {'time': '0.000', 'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED'}, {'time': '0.026', 'sql': 'SELECT AVG(book_order.price) AS bookorder__price__avg FROM book LEFT OUTER JOIN book_order ON (book.id = book_order.book_id)'}]
'''
return HttpResponse('Successful!')

结果发现,这段代码的处理结果(查看SQL语句),是获取了所有图书销售价格的平均值,这显然不是我们想要的

A. aggregate---表示聚合,统计字段的所有值,调用计算函数处理,例如Count,Avg,Sum等等#获取字段的所有数据,并用聚合函数处理
B. annotate(注释)---为每一个字段添加一个新的属性,在这个属性里,可以进行计算函数处理#每行数据添加字段,分组(group by),然后聚合函数处理
示例:

from django.shortcuts import render
from .models import *
from django.http import HttpResponse
from django.db.models import Avg
from django.db import connection

def index(request):
# 获取每一本书销售的平均价格

# resulets是一个QuerySet对象
results=Book.objects.annotate(avg=Avg('bookorder__price'))
for result in results:
    # result.avg属性是在results生成的
    print('{0},{1}'.format(result.name,result.avg))
print(connection.queries)
return HttpResponse('Successful!')

'''
十万个为什么,130.0
生活知识百科,63.333333333333336
战争知识百科全书,None
天气预报常识,None
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}, {'sql': 'SELECT book.id, book.name, book.pages, book.price, book.rating, book.author_id, book.publisher_id, AVG(book_order.price) AS avg FROM book LEFT OUTER JOIN book_order ON (book.id = book_order.book_id) GROUP BY book.id ORDER BY NULL', 'time': '0.000'}]
'''

结果符合预期

注意,annotate这个方法,最好在跨表操作的时候应用
比如以下单表操作,结果不是预期的:

def index3(request):

# 给bookorder每一个对象添加一个属性,这个属性的作用是---统计price的平均值
results=BookOrder.objects.annotate(my_avg=Avg('price'))
for result in results:
    print(result.id,':',result.my_avg)
print(connection.queries)
return HttpResponse('index3')

'''

1 : 220.0
2 : 215.0
3 : 230.0
4 : 180.0
5 : 205.0
6 : 220.0
7 : 222.0
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}, {'sql': 'SELECT book_order.id, book_order.book_id, book_order.price, AVG(book_order.price) AS my_avg FROM book_order GROUP BY book_order.id ORDER BY NULL', 'time': '0.001'}]

'''
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容