● 外键的删除操作---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'替换成你自己的数据库名称
<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'}]
'''