0%

python django实践二

说明

紧接python django实践

聚合和分组查询

聚合查询(aggregate)

  • 聚合查询函数是对一组值执行计算,并返回单个值

  • Django 使用聚合查询前要先从 django.db.models 引入 Avg、Max、Min、Count、Sum(首字母大写)。

  • 计算所有图书的平均价格:

1
2
3
4
5
from django.db.models import Avg,Max,Min,Count,Sum  #   引入函数
...
res = models.Book.objects.aggregate(Avg("price"))
print(res, type(res))
...
  • 计算所有图书的数量、最贵价格和最便宜价格:
1
2
res=models.Book.objects.aggregate(c=Count("id"),max=Max("price"),min=Min("price"))
print(res,type(res)

分组查询(annotate)

  • 分组查询一般会用到聚合函数,所以使用前要先从 django.db.models 引入 Avg,Max,Min,Count,Sum(首字母大写)
1
from django.db.models import Avg,Max,Min,Count,Sum  #   引入函数

返回值:

  • 分组后,用 values 取值,则返回值是 QuerySet 数据类型里面为一个个字典;
  • 分组后,用 values_list 取值,则返回值是 QuerySet 数据类型里面为一个个元组。

MySQL 中的 limit 相当于 ORM 中的 QuerySet 数据类型的切片。

annotate 里面放聚合函数。

  • values 或者 values_list 放在 annotate 前面:values 或者 values_list 是声明以什么字段分组,annotate 执行分组。
  • values 或者 values_list 放在annotate后面: annotate 表示直接以当前表的pk执行分组,values 或者 values_list 表示查询哪些字段, 并且要将 annotate 里的聚合函数起别名,在 values 或者 values_list 里写其别名。

实例

  • 统计每一个出版社的最便宜的书的价格:
1
2
3
4
res = models.Publish.objects.values("name").annotate(in_price = Min("book__price"))
print(res)

<QuerySet [{'name': '菜鸟出版社', 'in_price': Decimal('100.00')}, {'name': '明教出版社', 'in_price': Decimal('300.00')}]>
  • 统计每一本书的作者个数:
1
2
3
4
res = models.Book.objects.annotate(c = Count("authors__name")).values("title","c")
print(res)

<QuerySet [{'title': '菜鸟教程', 'c': 1}, {'title': '吸星大法', 'c': 1}, {'title': '冲灵剑法', 'c': 1}]>
  • 统计每一本以”菜”开头的书籍的作者个数:
1
2
res = models.Book.objects.filter(title__startswith="菜").annotate(c = Count("authors__name")).values("title","c")
print(res)
  • 统计不止一个作者的图书名称:
1
2
3
4
res = models.Book.objects.annotate(c = Count("authors__name")).filter(c__gt=0).values("title","c")
print(res)

<QuerySet [{'title': '菜鸟教程', 'c': 1}, {'title': '吸星大法', 'c': 1}, {'title': '冲灵剑法', 'c': 1}]>
  • 根据一本图书作者数量的多少对查询集 QuerySet 进行降序排序:
1
2
res = models.Book.objects.annotate(c = Count("authors__name")).order_by("-c").values("title","c")
print(res)
  • 查询各个作者出的书的总价格:
1
2
res = models.Author.objects.annotate(all = Sum("book__price")).values("name","all")
print(res)

F() 查询

  • F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值

  • F 动态获取对象字段的值,可以进行运算。

  • Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取余的操作。

  • 修改操作(update)也可以使用 F() 函数。

实例

  • 查询工资大于年龄的人:
1
2
3
4
from django.db.models import F
...
book=models.Emp.objects.filter(salary__gt=F("age")).values("name","age")
...
  • 将每一本书的价格提高100元:
1
2
res = models.Book.objects.update(price=F("price")+100)
print(res)

Q() 查询

  • 之前构造的过滤器里的多个条件的关系都是 and,如果需要执行更复杂的查询(例如 or 语句),就可以使用 Q 。

  • Q 对象可以使用 & | ~ (与 或 非)操作符进行组合。优先级从高到低:~ & |。

实例

  • 查询价格大于 350 或者名称以菜开头的书籍的名称和价格。
1
2
3
4
from django.db.models import Q

res=models.Book.objects.filter(Q(price__gt=350)|Q(title__startswith="菜")).values("title","price")
print(res)
  • 查询以”菜”结尾或者不是 2010 年 10 月份的书籍:
1
2
res = models.Book.objects.filter(Q(title__endswith="菜") | ~Q(Q(pub_date__year=2010) & Q(pub_date__month=10)))
print(res)
  • 查询出版日期是 2004 或者 1999 年,并且书名中包含有”菜”的书籍。
1
2
res = models.Book.objects.filter(Q(pub_date__year=2004) | Q(pub_date__year=1999), title__contains="菜")
print(res)

Q 对象和关键字混合使用,Q 对象要在所有关键字的前面:

中间件-验证登录

  • StudyDjanog/TestModel/model.py 加入user
1
2
3
4
class Users(models.Model):
name = models.CharField(max_length=32)
pwd = models.CharField(max_length=32)

  • 生成表结构
1
2
python manage.py makemigrations TestModel  
python manage.py migrate TestModel
  • 登录代码 TestModel\views.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def user_login(request):
if request.method != "POST":
return JsonResponse({'code': -1, 'msg': 'method must is POST'})

data = json.loads(request.body)
user_name = data.get('username')
pwd = data.get("pwd")
print(data)
try:
user_entry = Users.objects.get(name=user_name, pwd=pwd)
if user_entry:
# 设置登录的session
request.session["user"] = user_name
return JsonResponse({'code': 1, 'msg': 'login is success'})
except ObjectDoesNotExist:
return JsonResponse({'code': -1, 'msg': 'login is fail111'})

  • 编写中间件 TestModel\middleware\authen.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from django.http import JsonResponse
from django.shortcuts import redirect,render
from django.utils.deprecation import MiddlewareMixin
import re

#白名单
#将登陆、登出、验证登陆请求设为白名单,不进行用户验证。
#这里设置了/static/静态文件,因为我这个项目静态文件没走nginx,如果静态文件通过nginx访问,可以不需要设置
exclued_path = ["/login/","/testModel/login/","/logout/","/login_ajax_check","/static/"]

#用来验证用户是否有权限登陆的中间件
class AuthenticationMiddle(MiddlewareMixin):
def process_request(self, request):
url_path = request.path
#如果不在请求在白名单里
if url_path not in exclued_path:
#如果未登陆,则调转到登陆页面,将请求的url作为next参数
# if not request.user.is_authenticated:
if not request.session.get("user"):
# return redirect("/login/?next={url_path}".format(url_path=url_path))
return JsonResponse({'code': -1, 'msg': 'login is failed2222'})
#如果已经登陆,则通过
else:
pass

  • 引用中间件 StudyDjanog\StudyDjanog\settings.py
1
2
3
4
5
6
7
8
9
10
11
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'TestModel.middleware.authen.AuthenticationMiddle', # 引用自定义中间件

]
  • 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests

data ={"username": "admin", "pwd": "123456"}
rs = requests.session()


resp = rs.post("http://127.0.0.1:8000/testModel/login/", json=data)
print(resp.text)

resp1 = rs.get("http://127.0.0.1:8000/testModel/userQuery")
print(resp1.text)


{"code": 1, "msg": "login is success"}
<p>runoob runoob </p>
  • 最终的session内容在django_session表中自动存储了

image-20230803172250422

其他