cookie与session,以及Q

目录

什么是cookie,cookie的应用场景及缺点

Django中如何使用cookie

Cookie使用示例

什么是session及session的工作原理

Django中如何使用会话session

Session使用示例

小结

HTTP协议本身是”无状态”的,在一次请求和下一次请求之间没有任何状态保持,服务器无法识别来自同一用户的连续请求。有了cookie和session,服务器就可以利用它们记录客户端的访问状态了,这样用户就不用在每次访问不同页面都需要登录了。

什么是cookie,cookie的应用场景及缺点
cookie是一种数据存储技术, 它是将一段文本保存在客户端(浏览器或本地电脑)的一种技术,并且可以长时间的保存。当用户首次通过客户端访问服务器时,web服务器会发送给客户端的一小段信息。客户端浏览器会将这段信息以cookie形式保存在本地某个目录下的文件内。当客户端下次再发送请求时会自动将cookie也发送到服务器端,这样服务器端通过查验cookie内容就知道该客户端之前访问过了。

cookie的常见应用场景包括:

判断用户是否已经登录
记录用户登录信息(比如用户名,上次登录时间)
记录用户搜索关键词
cookie的缺点在于其并不可靠和不安全,主要原因如下:

浏览器不一定会保存服务器发来的cookie,用户可以通过设置选择是否禁用cookie。
cookie是有生命周期的(通过Expire设置),如果超过周期,cookie就会被清除。
HTTP数据通过明文发送,容易受到攻击,因此不能在cookie中存放敏感信息(比如信用卡号,密码等)。
cookie以文件形式存储在客户端,用户可以随意修改的。
Django中如何使用cookie
第一步:提供响应数据时设置cookie(保存到客户端)

response.set_cookie(cookie_name, value, max_age = None, expires = None) 
 
# key : cookie的名称
# value : 保存的cookie的值
# max_age: 保存的时间,以秒为单位
# expires: 过期时间,为datetime对象或时间字符串
例子: response.set_cookie('username','John',600)

注意:Django的视图默认返回的response是不包含cookie的,需手动调用set_cookie方法。

下面是3个设置cookie的例子:

# 例子1:不使用模板
response = HttpResponse("hello world")
response.set_cookie(key,value,max_age)
return response
 
# 例子2: 使用模板
response = render(request,'xxx.html', context)
response.set_cookie(key,value,max_age)
return response
 
# 例子3: 重定向
response = HttpResponseRedirect('/login/')
response.set_cookie(key,value,max_age)
return response
第二步: 获取COOKIES中的数据, 进行处理验证

# 方法一
request.COOKIES['username']
 
# 方法二
request.COOKIES.get('username','')
客户端再次发送请求时,request会携带本地存储的cookie信息,视图中你可以通过request.COOKIES获取。

为了防止获取不能存在的Key报错,你可以通过如下方式检查一个cookie是否已存在。

request.COOKIES.has_key('cookie_name')
如果你希望删除某个cookie,你可以使用如下方法:

response.delete_cookie('username')
Cookie使用示例
下面是django中使用cookie验证用户是否已登录的一个示例。用户首次登录时设置cookie,再次请求时验证请求携带的cookie。

# 如果登录成功,设置cookie
def login(request):
    if request.method == 'POST':
        form = LoginForm(request.POST)
        
        if form.is_valid():
            username = form.cleaned_data['username']
            password = form.cleaned_data['password']
            user = User.objects.filter(username__exact=username, password__exact=password)
 
            if user:
                response = HttpResponseRedirect('/index/')
                # 将username写入浏览器cookie,有效时间为360秒
                response.set_cookie('username', username, 360)
                return response
            else:
                return HttpResponseRedirect('/login/')
                                                           
    else:
        form = LoginForm()
 
    return render(request, 'users/login.html', {'form': form})
 
 
# 通过cookie判断用户是否已登录
def index(request):
    # 读取客户端请求携带的cookie,如果不为空,表示为已登录帐号
    username = request.COOKIES.get('username', '')
    if not username:
        return HttpResponseRedirect('/login/')
    return render(request, 'index.html', {'username': username})
什么是session及session的工作原理
session又名会话,其功能与应用场景与cookie类似,用来存储少量的数据或信息。但由于数据存储在服务器上,而不是客户端上,所以比cookie更安全。不过当用户量非常大时,所有的会话信息都存储于服务器会对服务器造成一定的压力。

Django中如何使用会话session
第一步:检查基本设置

Django中使用session首选需要确保settings.py中已开启了SessionMiddleware中间件。

'django.contrib.sessions.middleware.SessionMiddleware',
Django默认使用数据库存储每个session的sessionid, 所以你还需确保INSTALLED_APPS 是包含如下app:

'django.contrib.sessions',
当然你还可以使用更快的文件或缓存来存储会话信息,可以通过SESSION_ENGINE设置就行。

第二步:使用session

request.session是一个字典,你可以在视图和模板中直接使用它。

# 设置session的值
request.session['key'] = value
request.session.set_expiry(time): 设置过期时间,0表示浏览器关闭则失效
 
# 获取session的值
request.session.get('key',None)
 
# 删除session的值, 如果key不存在会报错
del request.session['key']
 
# 判断一个key是否在session里
'fav_color' in request.session
 
# 获取所有session的key和value
request.session.keys()
request.session.values()
request.session.items()

另外,settings.py 还有两项有关session比较重要的设置: 1、SESSION_COOKIE_AGE:以秒为单位,session的有效时间,可以通过set_expiry 方法覆盖。 2、SESSION_EXPIRE_AT_BROWSER_CLOSE:默认为Flase,是否设置为浏览器关闭,会话自动失效。

Session使用示例
下面是django中使用session进行用户登录和登出的一个示例。用户首次登录时设置session,退出登录时删除session。

# 如果登录成功,设置session
def login(request):
    if request.method == 'POST':
        form = LoginForm(request.POST)
 
        if form.is_valid():
            username = form.cleaned_data['username']
            password = form.cleaned_data['password']
            user = User.objects.filter(username__exact=username, password__exact=password)
            if user:
                # 将username写入session,存入服务器
                request.session['username'] = username
                return HttpResponseRedirect('/index/')
            else:
                return HttpResponseRedirect('/login/')
    else:
        form = LoginForm()
 
    return render(request, 'users/login.html', {'form': form})
 
 
# 通过session判断用户是否已登录
def index(request):
    # 获取session中username
    username = request.session.get('username', '')
    if not username:
        return HttpResponseRedirect('/login/')
    return render(request, 'index.html', {'username': username})
 
# 退出登录
def logout(request):
    try:
        del request.session['username']
    except KeyError:
        pass
    return HttpResponse("You're logged out.")

下面是通过session控制不让用户连续评论两次的例子。实际应用中我们还可以通过session来控制用户登录时间,记录访问历史,记录购物车信息等等。

from django.http import HttpResponse
 
def post_comment(request, new_comment):
    if request.session.get('has_commented', False):
        return HttpResponse("You've already commented.")
    c = comments.Comment(comment=new_comment)
    c.save()
    request.session['has_commented'] = True
    return HttpResponse('Thanks for your comment!')
小结
cookie和session都是一种存储少量数据的技术,用来记录客户端的访问状态,区别在于一个存储在客户端,一个存储在服务器端。Django中使用cookie和session都非常方便,都是基于先设置再获取的原则,可以灵活地用于各个场景。

目录

什么是QuerySet

Django的QuerySet是惰性的

Django的QuerySet自带缓存(Cache)

用if也会导致queryset的执行

统计查询结果数量优选count方法

当queryset非常大时,数据请按需去取

更新数据库部分字段请用update方法

批量创建或更新数据请用bulk_create或bulk_update

专业地使用explain方法

小结

什么是QuerySet
QuerySet是Django提供的强大的数据库接口(API)。正是因为通过它,我们可以使用filter, exclude, get等方法进行数据库查询,而不需要使用原始的SQL语言与数据库进行交互。从数据库中查询出来的结果一般是一个集合,这个集合叫就做 queryset。

如果你还不知道如何使用Django提供的数据接口(API)对数据库进行最基本的增删改查,请先阅读下面这篇文章:

://pythondjango.cn/django/basics/6-models-queryset-API/
Django的QuerySet是惰性的
Django的QuerySet是惰性的, 那么它到底是什么意思呢?

下例中article_list试图从数据库查询一个标题含有django的全部文章列表。

article_list = Article.objects.filter(title__contains="django")
但是当我们定义article_list的时候,Django的数据接口QuerySet并没有对数据库进行任何查询。无论你加多少过滤条件,Django都不会对数据库进行查询。只有当你需要对article_list做进一步运算时(比如打印出查询结果,判断是否存在,统计结果长度),Django才会真正执行对数据库的查询(见下例1)。这个过程被称为queryset的执行(evaluation)。Django这样设计的本意是尽量减少对数据库的无效操作,比如查询了结果而不用是对计算资源的很大浪费。

# example 1
for article in article_list:
    print(article.title)
    
Django的QuerySet自带缓存(Cache)
在例1中,当你遍历article_list时,所有匹配的记录会从数据库获取。这些结果会载入内存并保存在queryset内置的cache中。这样如果你再次遍历或读取这个article_list时,Django就不需要重复查询了,这样也可以减少对数据库的查询。

下例中例2比例3要好,因为在你打印文章标题后,Django不仅执行了查询,还把查询到的article_list放在了缓存里, 因此这个article_list是可以复用的。例3就不行了。

# Example 2: Good
article_list = Article.objects.filter(title__contains="django")
for article in article_list:
    print(article.title)
 
# Example 3: Bad
for article in Article.objects.filter(title__contains="django"):
    print(article.title)
用if也会导致queryset的执行
不知道你注意到上述例2中有个问题没有?万一article_list是个空数据集呢? 虽然for....in...用到空集合上也不会出现raise什么错误,但专业优秀的我们怎么能允许这样的低级事情发生呢?最好的做法就是在loop前加个if判断(例4)。因为django会对执行过的queryset进行缓存(if也会导致queryset执行, 缓存article_list),所以我们在遍历article_list时不用担心Django会对数据库进行二次查询。

# Example 4: Good
article_list = Article.objects.filter(title__contains="django")
if article_list:
    for article in article_list:
        print(article.title)
else:
    print("No records")
但有时我们只希望了解查询的结果是否存在,而不需要使用整个数据集,这时if触发整个queryset的缓存变成了一件坏事情。哎,程序员要担心的事情着不少。这时你可以用exists()方法。与if判断不同,exists只会检查查询结果是否存在,返回True或False,而不会缓存article_list(见例5)。

# Example 5: Good
article_list = Article.objects.filter(title__contains="django")
if article_list.exists():
    print("Records found.")
else:
    print("No records")
注意: 判断查询结果是否存在到底用if还是exists取决于你是否希望缓存查询数据集复用,如果是用if,反之用exists。

统计查询结果数量优选count方法
len()与count()均能统计查询结果的数量。一般来说count更快,因为它是从数据库层面直接获取查询结果的数量,而不是返回整个数据集,而len会导致queryset的执行,需要将整个queryset载入内存后才能统计其长度。但事情也没有绝对,如果数据集queryset已经在缓存里了,使用len更快,因为它不需要跟数据库再次打交道。

下面三个例子中,只有例7最差,尽量不要用。

# Example 6: Good
count = Article.objects.filter(title__contains="django").count()
 
# Example 7:Bad
count = Article.objects.filter(title__contains="django").len()
 
# Example 8: Good
article_list = Article.objects.filter(title__contains="django")
if article_list:
    print("{} records found.".format(article_list.len()))
当queryset非常大时,数据请按需去取
当查询到的queryset的非常大时,会大量占用内存(缓存)。我们可以使用values和value_list方法按需提取数据。比如例1中我们只需要打印文章标题,这时我们完全没有必要把每篇文章对象的全部信息都提取出来载入到内存中。我们可以做如下改进, 查询数据库时只提取title出来(例9)。

# Example 9: Good
article_list = Article.objects.filter(title__contains="django").values('title')
if article_list:
    print(article.title)
 
article_list = Article.objects.filter(title__contains="django").values_list('id', 'title')
if article_list:
    print(article.title)
注意: values和values_list分别以字典和元组形式返回查询结果,不再是queryset类型数据。

我们还可以使用defer和only这两个查询方法来实现按需查询数据。除此以外,我们还可以使用iterator()方法可以优化程序对内存的使用,其工作原理是不对queryset进行缓存,而是采用迭代方法逐一返回查询结果,但这有时会增加数据库的访问次数,新手一般也驾驭不了。我这里就不细讲了。

更新数据库部分字段请用update方法
如果需要对数据库中的某条已有数据或某些字段进行更新,更好的方式是用update,而不是save方法。我们现在可以对比下面两个案例。例10中需要把整个Article对象的数据(标题,正文…..)先提取出来,缓存到内存中,变更信息后再写入数据库。而例11直接对标题做了更新,不需要把整个文章对象的数据载入内存,显然更高效。尽管单篇文章占用内存不多,但是万一用户非常多呢,那么占用的内存加起来也是很恐怖的。

# Example 10: Bad
article = Article.objects.get(id=10)
Article.title = "Django"
article.save()
 
# Example 11: Good
Article.objects.filter(id=10).update(title='Django')
update方法还会返回已更新条目的数量,这点也非常有用。当然事情也没有绝对,save方法对于单个模型的更新还是很有优势的,比如save(commit=False), article.author = request.user等等事情update都做不来。

批量创建或更新数据请用bulk_create或bulk_update
在Django中向数据库中插入或更新多条数据时,每使用save或create方法保存一条就会执行一次SQL。而Django提供的bulk_create和bulk_update方法可以一次SQL添加或更新多条数据,效率要高很多,如下所示:

# 内存生成多个对象实例
articles  = [Article(title="title1", body="body1"), Article(title="title2", body="body2"), Article(title="title3", body="body3")]
 
# 执行一次SQL插入数据
Article.objects.bulk_create(articles)
专业地使用explain方法
Django 2.1中QuerySet新增了explain方法,可以统计一个查询所消耗的执行时间。这可以帮助程序员更好地优化查询结果。

print(Blog.objects.filter(title='My Blog').explain(verbose=True))
 
# outputt
Seq Scan on public.blog  (cost=0.00..35.50 rows=10 width=12) (actual time=0.004..0.004 rows=10 loops=1)
  Output: id, title
  Filter: (blog.title = 'My Blog'::bpchar)
Planning time: 0.064 ms
Execution time: 0.058 ms
小结
Django QuerySet的惰性和缓存特性对于减少数据库的访问次数非常有用。你需要根据不同应用场景选择合适的方法(比如exists, count, update, values) 来减少数据库的访问,减少查询结果占用的内存空间从而提升网站的性能。希望本文总结的一些高效使用queryset技巧对你学习Django和Web开发有所帮助。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.youkuaiyun.com/heludoit1/article/details/146430976

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值