跨站请求伪造
django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。django中设置防跨站请求伪造功能有分为全局和局部。
html中设置Token:
{
%
csrf_token
%
}
全局:
中间件 django.middleware.csrf.CsrfViewMiddleware
局部:
- @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
- @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
from django.views.decorators.csrf import csrf_exempt,csrf_protect
文件上传
上传到代码目录
1 <form action="/lx/upload/" method="post" enctype="multipart/form-data"> 2 {% csrf_token %} 3 <input type="file" name="fafa"> 4 <input type="submit" value="go"> 5 </form>
1 def upload_file(request): 2 if request.method == "POST": 3 obj = request.FILES.get('fafa') 4 print("obj.name:", obj.name) 5 f = open(obj.name, 'wb') 6 for chunk in obj.chunks(): 7 f.write(chunk) 8 f.close() 9 return render(request,'lx/file.html')
上传到数据库
1 from django import forms 2 from lx import models 3 4 5 class FileForm(forms.Form): 6 ExcelFile = forms.FileField() 7 8 9 def upload(request): 10 uf = FileForm(request.POST,request.FILES) 11 12 if uf.is_valid(): 13 u_file = models.UploadFile() 14 u_file.userid = 1 15 u_file.file = uf.cleaned_data['ExcelFile'] 16 u_file.save() 17 print(u_file.file) 18 else: 19 print('uf:', uf) 20 21 return render(request, 'lx/file.html', {'uf': uf})
1 <form action="/lx/upload2/" method="post" enctype="multipart/form-data"> 2 {% csrf_token %} 3 <p>{{ uf }}</p> 4 <input type="submit" value="go"> 5 </form>
1 class UploadFile(models.Model): 2 userid = models.CharField(max_length=30) 3 file = models.FileField(upload_to='./upload2/') 4 date = models.DateTimeField(auto_now_add=True)
Session
Django中默认支持Session,内部提供了5种类型的Session供开发者使用:
数据库(默认) 缓存 文件 缓存 + 数据库 加密cookie
数据库Session
1 SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) 2 3 SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) 4 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) 5 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) 6 #SESSION_COOKIE_DOMAIN = '.baidu.com' 7 SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) 8 SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) 9 SESSION_COOKIE_AGE = 86400 * 14 # Session的cookie失效日期(2周)(默认) 10 SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认) 11 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
缓存Session
1 CACHES = { 2 'default': { # 这个地方必须是'default',否则会报错 3 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 4 'LOCATION': [ 5 '127.0.0.1:11211', 6 '127.0.0.1:11212', 7 ] 8 9 } 10 } 11 12 SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎 13 SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置 14 15 SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 16 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径 17 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 18 SESSION_COOKIE_SECURE = False # 是否Https传输cookie 19 SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输 20 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) 21 SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期 22 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存
文件Session(文件路径必须存在)
1 SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎 2 SESSION_FILE_PATH = os.path.join(BASE_DIR, 'cache') 3 # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() 4 # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T 5 6 SESSION_COOKIE_NAME ="sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 7 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径 8 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 9 SESSION_COOKIE_SECURE = False # 是否Https传输cookie 10 SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输 11 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) 12 SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期 13 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存
缓存+数据库Session
1 CACHES = { 2 # 'default': { 3 # 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 4 # 'LOCATION': os.path.join(BASE_DIR, 'cache_file'), 5 # 'TIMEOUT': 600, 6 # 'OPTIONS': { 7 # 'MAX_ENTRIES': 1000 8 # } 9 # } 10 'default': { 11 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 12 'LOCATION': [ 13 '127.0.0.1:11211', 14 '127.0.0.1:11212', 15 ] 16 17 } 18 } 19 20 SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎
加密cookies Session
1 SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎
session操作如下:
1 def index(request): 2 # request.session['k1'] = 123 # 赋值 3 # 4 # v1 = request.session['k1'] 5 6 # v2 = request.session.get('k1',None) # 不存在则为None 7 # 8 # v3 = request.session.setdefault('k1', 456) # 存在则不设置 9 # print(v2, v3) 10 # del request.session['k1'] 11 12 # 所有键、值 、键值对, 以列表的形式返回 13 # request.session.setdefault('k1', 456) 14 # v1 = request.session.keys() 15 # v2 = request.session.values() 16 # v3 = request.session.items() 17 # print(v1,v2,v3) 18 19 # v1 = request.session.session_key # 用户session的随机字符串 20 # v2 = request.session.clear_expired() # 将所有Session失效日期小于当前日期的数据删除 21 # v4 = request.session.exists('7m1k9hervqps7l131x22tnu4hvz1bf9j') # 检查 用户session的随机字符串 在数据库中是否 22 # v5 = request.session.delete('session_key') # 删除当前用户的所有Session数据 23 # # request.session.set_expiry(value) 24 # # *如果value是个整数,session会在些秒数后失效。 25 # # *如果value是个datatime或timedelta,session就会在这个时间后失效。 26 # # *如果value是0, 用户关闭浏览器session就会失效。 27 # # *如果value是None, session会依赖全局session失效策略。 28 29 # print(v1, v2, v3, v4, v5) 30 31 return HttpResponse("OK")
django内置分页扩展
1 from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger 2 3 4 class CustomPaginator(Paginator): 5 def __init__(self, current_page, max_pager_num, *args, **kwargs): 6 """ 7 :param current_page: 当前页 8 :param max_pager_num: 最多显示的页码个数 9 :param args: 10 :param kwargs: 11 """ 12 self.current_page = int(current_page) 13 self.max_pager_num = max_pager_num 14 super(CustomPaginator, self).__init__(*args, **kwargs) 15 16 def page_num_range(self): 17 # 当前页面 18 # self.current_page 19 # 总页数 20 # self.num_pages 21 # 最多显示的页码个数 22 # self.max_pager_num 23 print(1) 24 if self.num_pages < self.max_pager_num: 25 return range(1, self.num_pages + 1) 26 print(2) 27 part = int(self.max_pager_num / 2) 28 if self.current_page - part < 1: 29 return range(1, self.max_pager_num + 1) 30 print(3) 31 if self.current_page + part > self.num_pages: 32 return range(self.num_pages + 1 - self.max_pager_num, self.num_pages + 1) 33 print(4) 34 return range(self.current_page - part, self.current_page + part + 1) 35 36 37 L = [] 38 for i in range(999): 39 L.append(i) 40 41 42 def page(request): 43 current_page = request.GET.get('p', 1) 44 # paginator = Paginator(L,10) 45 paginator = CustomPaginator(current_page, 11, L, 10) 46 # per_page: 每页显示条目数量 47 # count: 数据总个数 48 # num_pages:总页数 49 # page_range:总页数的索引范围,如: (1,10),(1,200) 50 # page: page对象 51 52 try: 53 print('current_page:', current_page) 54 posts = paginator.page(current_page) 55 # has_next 是否有下一页 56 # next_page_number 下一页页码 57 # has_previous 是否有上一页 58 # previous_page_number 上一页页码 59 # object_list 分页之后的数据列表 60 # number 当前页 61 # paginator paginator对象 62 except PageNotAnInteger: 63 posts = paginator.page(1) 64 except EmptyPage: 65 posts = paginator.page(paginator.num_pages) 66 67 print('posts:', posts, 'type posts:', type(posts)) 68 69 return render(request, 'lx/page.html', {'posts': posts})
1 <ul> 2 {% for item in posts %} 3 <li>{{ item}}</li> 4 {% endfor %} 5 </ul> 6 7 <div class="pagination"> 8 <span class="step-links"> 9 {% if posts.has_previous %} 10 <a href="?p={{ posts.previous_page_number }}">Previous</a> 11 {% endif %} 12 13 {% for i in posts.paginator.page_num_range %} 14 <a href="?p={{ i }}">{{ i }}</a> 15 {% endfor %} 16 17 {% if posts.has_next %} 18 <a href="?p={{ posts.next_page_number }}">Next</a> 19 {% endif %} 20 </span> 21 22 <span class="current"> 23 Page {{ posts.number }} of {{ posts.paginator.num_pages }}. 24 </span> 25 26 </div>
序列化
Django为我们提供了一个强大的序列化工具serializers,支持三种序列化格式:xml、json、yaml serialize方法至少接收两个参数, 第一: 要序列化成为的数据格式,这里是‘xml’ 第二: 要序列化的数据对象,数据通常是ORM模型的QuerySet,一个可迭代的对象 序列化数据: from django.core import serializers data = serializers.serialize("xml", models.UserType.objects.all()) 序列化数据写入文件: with open("file.xml", "w") as out: xml_serializer.serialize(models.UserType.objects.all(), stream=out) 序列化指定字段: from django.core import serializers data = serializers.serialize('xml', models.UserType.objects.all(), fields=('name','size')) 序列化继承模型 class Place(models.Model): name = models.CharField(max_length=50) class Restaurant(Place): serves_hot_dogs = models.BooleanField(default=False) ############ 序列化Restaurant中属性 ############ data = serializers.serialize('xml', Restaurant.objects.all()) ############ 序列化Restaurant与Place中属性 ############ all_objects = list(Restaurant.objects.all()) + list(Place.objects.all()) data = serializers.serialize('xml', all_objects) 反序列化: # deserialize()方法返回的是一个迭代器 for obj in serializers.deserialize("xml", data): print(obj)
json.dumps无法处理datetime日期,可以通过自定义处理器来做扩展。
1 import json 2 from datetime import date 3 from datetime import datetime 4 5 6 class JsonCustomEncoder(json.JSONEncoder): 7 def default(self, field): 8 if isinstance(field, datetime): 9 return field.strftime('%Y-%m-%d %H:%M:%S') 10 elif isinstance(field, date): 11 return field.strftime('%Y-%m-%d') 12 else: 13 return json.JSONEncoder.default(self, field) 14 15 16 d = date.today() 17 d2 = datetime.now() 18 print(d, d2) 19 ds = json.dumps(d, cls=JsonCustomEncoder) 20 ds2 = json.dumps(d2, cls=JsonCustomEncoder) 21 # ds = json.dumps(d) 22 print("ds:", ds) 23 print('ds2:', ds2)
信号
Django中提供了“信号调度”,用于在框架执行操作时解耦。就是一些动作发生的时候,去执行一些操作。
Model signals pre_init # django的modal执行其构造方法前,自动触发 post_init # django的modal执行其构造方法后,自动触发 pre_save # django的modal对象保存前,自动触发 post_save # django的modal对象保存后,自动触发 pre_delete # django的modal对象删除前,自动触发 post_delete # django的modal对象删除后,自动触发 m2m_changed # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发 class_prepared # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发 Management signals pre_migrate # 执行migrate命令前,自动触发 post_migrate # 执行migrate命令后,自动触发 Request/response signals request_started # 请求到来前,自动触发 request_finished # 请求结束后,自动触发 got_request_exception # 请求异常后,自动触发 Test signals setting_changed # 使用test测试修改配置文件时,自动触发 template_rendered # 使用test测试渲染模板时,自动触发 Database Wrappers connection_created # 创建数据库连接时,自动触发
信号signal实例:
方法一(通过receiver()装饰器进行注册): from django.core.signals import request_finished from django.dispatch import receiver @receiver(request_finished) def my_callback(sender, **kwargs): print("Resquest finished!") 方法二(手动注册): from django.core.signals import request_finished def my_callback(sender, **kwargs): print("Resquest finished!") request_finished.connect(my_callback) # Signal.connect(receiver, sender=None, weak=True, dispatch_uid=None)[source] # receiver :当前信号连接的回调函数,也就是处理信号的函数。 # sender :指定从哪个发送方接收信号。 # weak : 是否弱引用 # dispatch_uid :信号接收器的唯一标识符,以防信号多次发送。
1 from django.db.models.signals import pre_save 2 from django.dispatch import receiver 3 from .models import UploadFile 4 5 @receiver(pre_save, sender=UploadFile) 6 def my_callback(sender, **kwargs): 7 print("Resquest finished!")
防止重复信号
# 每个唯一的dispatch_uid值,接收器都只绑定到信号一次 from django.core.signals import request_finished request_finished.connect(my_callback,dispatch_uid="my_unique_identifier")
自定义信号
1 from django.shortcuts import HttpResponse 2 import time 3 import django.dispatch 4 from django.dispatch import receiver 5 6 work_done = django.dispatch.Signal(providing_args=['path', 'time']) 7 8 def create_signal(request): 9 url_path = request.path 10 print("我已经做完了工作。现在我发送一个信号出去,给那些指定的接收器。") 11 12 work_done.send(create_signal, path=url_path, time=time.strftime("%Y-%m-%d %H:%M:%S")) 13 return HttpResponse("200, OK") 14 15 16 @receiver(work_done, sender=create_signal) 17 def my_callback(sender, **kwargs): 18 print("我在%s时间收到来自%s的信号,请求url为%s" % (kwargs['time'], sender, kwargs["path"]))