django 中的聚合和分组 F查询 Q查询 事务cookies和sessions 066
1 聚合和分组
聚合:对一些数据进行整理分析 进而得到结果(mysql中的聚合函数)
1aggregate(*args,**kwargs) :
通过对QuerySet进行计算 ,返回一个聚合值的字典.其中每一个参数都指定一个包含在字典中的返回值.即在查询集上生成聚合
from django.db.models import Avg,Min,Sum,Max # 从整个查询集生成统计值.比如,你想要计算所有在售书籍的平均价钱.django的查询语法提供了一种方式描述所有图书的集合 Book.objects.all().aggregate(Avg('price')) #{'print_avg':34.35} #aggregate()字句的参数描述了我们想要计算的聚合值 它是一个终止字句.意思是说,他返回一个包含一些键值对的字典 键的名称是按照字段和聚合函数的名称自动生成出来的如果想指定一个名称 可以按照这样的方式命名: Book.objects.aggregate(average_price=Avg('price')) #也可以查询多个值 即想知道所有图书价格的最大值和最小值 也可以 Book.objects.aggregate(Avg('price'),Max('price'),Min('price'))
分组:annotate(*args,**kwargs)
可以通过查询结果中每一个对象 所关联的对象集合,从而得出总计值(也可以是平均值或者总和),即为查询集的每一项生成聚合
#案例1:查询卢俊义出的书的总价格 Boo.objects.filter(author__name='卢俊义').aggregate(Sum('price')) #案例2:查各个作者出的书的总价格 这就涉及到分组了,条件是authors__name Book.objects.values('authors__name').annotate(Sum('price')) #案例3:查询各个出版社最便宜的书的价格 Book.objects.values('publisher__name').annotate(Min('price'))
F查询和Q查询
首先需要导入F
from django.db.models import F
#第一种用法 查询 可以结合前边的基于字段或者正反向查询都可以实现 # 动态的获取字段的值 比较表格中两列的值 ret = models.Book.objects.filter(kucun__gt =F('sale')).values() #该语句想要获取的是库存量大于销售量的书籍 # 第二种用法:对某列数据进行操作 #刷单 提高书籍的销售量 但是不改变库存 将某一列进行批量操作 modles.Book.objects.update(sale=F('sale')*2) # 此语句可以将书籍销量那一列进行更新 # 也可以进行单列更新 obj = models.Book.objects.filter(id=1).first() obj.sale = 60 obj.save() update和save()的区别 #前者只是对限制查询的列表列字段进行更新修改 而save是将所有的字段都
#进行了一遍的更新 因此欠着的效率要比后者高一点点 前者也可以实现后者
#的功能 即在前边加一个all() 就实现了全部刷新 即 models.Book.objects.all().update(sale=1000) #可实现对表格的所有的字段进行刷新
Q查询
首先需要导入Q
from django.db.models import Q
#构建Q的搜素条件 from django.db.models import Q # 1 Q对象(django.db.models.Q)可以对关键字参数进行封装,从而更好地应用多个查询 q1 = models.Book.objects.filter(Q(title__startswith='P')).all() print(q1) #[<Book:Python>,<Book:Perl>] #2 可以组合使用&(和),|(或)操作符,当一个操作符是用于两个Q的对象,它产生一个新的Q对象 Q(title__startswith='P') | Q(title__startswith='J') # 获取以P或者J开头的所有的对象 #3 Q对象可以用~操作符放在前面表示否定 也可以允许否定与不否定形式的足额 Q(title__startswith='P') |~Q(pub_date__year=2005) 查询以P开头的或者出版日期不是2005年的书籍 #4 应用范围: # Each lookup function that takes keyword-arguments (e.g. filter(), # exclude(), get()) can also be passed one or more Q objects as # positional (not-named) arguments. If you provide multiple Q object # arguments to a lookup function, the arguments will be “AND”ed # together. For example: Book.objects.get( Q(title__startswith='P'), Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) ) # sql: # SELECT * from polls WHERE title LIKE 'P%' # AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06') # import datetime # e=datetime.date(2005,5,6) #2005-05-06 #5Q对象可以和关键字参数查询一起使用 不过一定要把Q对象放在关键字参数查询的前面
事务
一系列的操作要么同时成功执行要么都不执行 保证原子性操作
from app01 import models from django.db import transaction try: with transaction.atomic(): models.Publisher.objects.create(name='新华出版社1') int('aaa') models.Publisher.objects.create(name='新华出版社2') models.Publisher.objects.create(name='新华出版社2') except Exception as e: print(e) #此时会走e #错误信息为 invalid literal for int() with base 10: 'aaa'
cookie
定义:保存在浏览器上的一组组键值对 是服务器发送出来存储在浏览器上的一组组键值对,下次访问服务器的时候浏览器会自动携带这些键值对 下次访问服务器的时候浏览器会自动携带这些键值对 以便服务器提取有用信息.
原理: 由服务器产生内容,浏览器接收后保存到本地,当浏览器再次访问时,浏览器会自动带上这些键值对 这样服务器就能通过cookie的内容来判断这个是谁了
特性:
1.服务器让浏览器保存的cookie
2.浏览器有权利进行不设置
3.下次访问时自动携带响应的cookie
django中操作cookie
1 获取cookie
request.COOKIES['key'] request.get_signed_cookie('key',default=RAISE_ERROR,salt='',max_age=None) #get_signed_cookie方法的参数: #default:默认值 #salt:加密盐 #max_age:后台控制过期时间
2 设置cookies
reo = HttpResponse(...) rep = render(request,....) rep.set_cookie(key,value,...) rep.set_signed_cookie(key,value,salt='加密盐',.....) # 参数 #key,键 #value='',值 #max_age=None,超时时间 #expires=None,超时时间(IE requires expires,so set it if hasn't been already) #path='/' cookie生效的路径 /表示根路径, 特殊的:根路径的cookie可以被任何url的页面访问 #domain=None,cookie生效的域名 secure=False,https传输 httponly=False 只能http协议传输 无法被js获取(不是绝对,底层抓包可以获取到也可以被覆盖)
3 删除cookie
def logout(request): rep = redirect(/'login/') rep.delete_cookie('user') # 删除用户浏览器上之前设置的user的cookoie值 return rep
4 案例 定义登录 通过cookie
# 做一个页面的访问之前的登录认证以及登录成功后直接进入之前要登录进入的页面 #流程 先登录 将登录信息以及cookie状态封装到装饰器里面再将装饰器加到要进入的页面上 从而实现登录才能访问某界面 # 先写装饰器 def login_required(func): def inner(request, *args, **kwargs): # 获取cookie is_login = request.COOKIES.get('is_login') # 不加密的cookie # 获取登录状态is_login = request.get_signed_cookie('is_login',salt='day66',default='xx') # 加密cookie return_url = request.path_info # 获取即将跳转的页面地址 if is_login == '1': ret = func(request, *args, **kwargs) else: ret = redirect('/login/?return_url={}'.format(return_url)) # login地址上携带上要跳转的地址 print(ret) print(return_url) return ret return inner #定义登录功能 def login(request): if request.method == 'POST': # 获取用户名和密码、要访问的页面 user = request.POST.get('user') pwd = request.POST.get('pwd') print(user) return_url = request.GET.get('return_url') # 此处的路径由上边的装饰器处获取 print(return_url) if user == 'alex' and pwd == '123': # 有跳转的地址时访问到跳转地址,否则访问index页面 if return_url: ret = redirect(return_url) else: ret = redirect('/index/') ret.set_cookie('is_login', '1') # 设置cookie 超时时间 max_age=5 路径 path='/home/' # ret.set_signed_cookie('is_login', '1', 'day66') # 设置加密cookie return ret return render(request, 'login.html') # 给响应页面加装饰器 @login_required def index(request): return HttpResponse('index') # 删除cookie def logout(request): ret = redirect('/login/') # 删除cookie ret.delete_cookie('is_login') # Set-Cookie: is_login=""; expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/ return ret
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="" method="post"> <p> 用户名:<input type="text" name="user"> </p> <p> 密码:<input type="password" name="pwd"> </p> <button>登录</button> </form> </body> </html>
session
cookie虽然在一定程度上解决了保持状态的需求 但是由于cookie本身最大支持4096字节 以及cookie本身保存在客户端 , 可能被拦截或窃取因此需要一种新的东西 能支持更多的字节,并且保存在服务器 有较高的安全性 即session
django中的session相关方法:
# 获取、设置、删除Session中数据 request.session['k1'] request.session.get('k1',None) request.session['k1'] = 123 request.session.setdefault('k1',123) # 存在则不设置 del request.session['k1'] # 所有 键、值、键值对 request.session.keys() request.session.values() request.session.items() request.session.iterkeys() request.session.itervalues() request.session.iteritems() # 会话session的key request.session.session_key # 将所有Session失效日期小于当前日期的数据删除 request.session.clear_expired() # 检查会话session的key在数据库中是否存在 request.session.exists("session_key") # 删除当前会话的所有Session数据 request.session.delete() # 删除当前的会话数据并删除会话的Cookie。 request.session.flush() 这用于确保前面的会话数据不可以再次被用户的浏览器访问 例如,django.contrib.auth.logout() 函数中就会调用它。 # 设置会话Session和Cookie的超时时间 request.session.set_expiry(value) * 如果value是个整数,session会在些秒数后失效。 * 如果value是个datatime或timedelta,session就会在这个时间后失效。 * 如果value是0,用户关闭浏览器session就会失效。 * 如果value是None,session会依赖全局session失效策略。
django中的session配置
1. 数据库Session SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) 2. 缓存Session SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎 SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置 3. 文件Session SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎 SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() 4. 缓存+数据库 SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎 5. 加密Cookie Session SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎 其他公用设置项: SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认) SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认) Django中Session相关设置
session登录案例
from django.shortcuts import render, redirect, HttpResponse from django.conf import global_settings, settings from django.contrib.sessions.backends import db def login_required(func): def inner(request, *args, **kwargs): # print(request.session.session_key) # 查看session的session_key # print(request.session.exists('r0f6ys1wj4du94w0fzr5lapyoxxzqr93')) # 判断对应的数据是否存在 is_login = request.session.get('is_login') # 获取session return_url = request.path_info if is_login == '1': ret = func(request, *args, **kwargs) else: ret = redirect('/login/?return_url={}'.format(return_url)) return ret return inner def login(request): if request.method == 'POST': user = request.POST.get('user') pwd = request.POST.get('pwd') print(user) return_url = request.GET.get('return_url') if user == 'alex' and pwd == '123': if return_url: ret = redirect(return_url) else: ret = redirect('/index/') request.session['is_login'] = '1' return ret return render(request, 'login.html') @login_required def home(request): # is_login = request.COOKIES.get('is_login') # if is_login != '1': # return redirect('/login/') return HttpResponse('home') @login_required def index(request): return HttpResponse('index') def logout(request): # request.session.delete() # 删除服务器的session数据,不删除cookie request.session.flush() # 删除session和cookie ret = redirect('/login/') # ret.delete_cookie('is_login') # Set-Cookie: is_login=""; expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/ return ret
该案例将数据存放在mysql数据库中 ,可以在数据库中看到 扎样的数据 分别是session的钥匙和数据