s14 django其他
Django其他篇
- 参考博客
Django高级:
https://www.cnblogs.com/wupeiqi/articles/5246483.html
- ModelForm组件
参考博客:
http://www.cnblogs.com/wupeiqi/articles/6229414.html
ModelForm(武sir博客)
a. class Meta:
model, # 对应Model的
fields=None, # 字段
exclude=None, # 排除字段
labels=None, # 提示信息
help_texts=None, # 帮助提示信息
widgets=None, # 自定义插件
error_messages=None, # 自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS)
field_classes=None # 自定义字段类 (也可以自定义字段)
localized_fields=('birth_date',) # 本地化,如:根据不同时区显示数据
如:
数据库中
2016-12-27 04:10:57
setting中的配置
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = True
则显示:
2016-12-27 12:10:57
b. 验证执行过程
is_valid -> full_clean -> 钩子 -> 整体错误
c. 字典字段验证
def clean_字段名(self):
# 可以抛出异常
# from django.core.exceptions import ValidationError
return "新值"
d. 用于验证
model_form_obj = XXOOModelForm()
model_form_obj.is_valid()
model_form_obj.errors.as_json()
model_form_obj.clean()
model_form_obj.cleaned_data
e. 用于创建
model_form_obj = XXOOModelForm(request.POST)
#### 页面显示,并提交 #####
# 默认保存多对多
obj = form.save(commit=True)
# 不做任何操作,内部定义 save_m2m(用于保存多对多)
obj = form.save(commit=False)
obj.save() # 保存单表信息
obj.save_m2m() # 保存关联多对多信息
f. 用于更新和初始化
obj = model.tb.objects.get(id=1)
model_form_obj = XXOOModelForm(request.POST,instance=obj)
...
PS: 单纯初始化
model_form_obj = XXOOModelForm(initial={...})
- 要点:
Model + Form => 验证 + 数据库操作
- class LoginModelForm(xxxxx):
利用model.A中的字段
1. 生成HTML标签:class Meta: ...
2. mf = xxxModelForm(instance=ModelObj)
3. 额外的标签, is_rmb = Ffields.CharField(widget=Fwidgets.CheckboxInput())
4. 各种验证 is_valid() -> 各种钩子...
5. mf.save()
# 或
instance = mf.save(False)
instance.save()
mf.save_m2m()
- 代码:
# views.py
url(r'^admin/', admin.site.urls),
url(r'^index/', views.index),
url(r'^user_list/', views.user_list),
url(r'^edit-(\d+)/', views.user_edit),
# model.py
from django.db import models
# Create your models here.
class UserType(models.Model):
caption = models.CharField(max_length=32)
class UserGroup(models.Model):
name = models.CharField(max_length=32)
class UserInfo(models.Model):
# username = models.CharField(verbose_name='用户',max_length=32)
username = models.CharField(verbose_name='用户',max_length=32)
email =models.EmailField()
user_type =models.ForeignKey(to='UserType',to_field='id')
u2g= models.ManyToManyField(UserGroup)
# views.py
from django.shortcuts import render,HttpResponse
from app01 import models
from django import forms
from django.forms import fields as Ffields
from django.forms import widgets as Fwidgets
# Create your views here.
class UserInfoModelForm(forms.ModelForm):
is_rmb = Ffields.CharField(
widget=Fwidgets.CheckboxInput()
)
class Meta:
model = models.UserInfo
fields = '__all__'
# fields = ['username','email'] # 指定部分字段
# exclude = ['username'] # 排除部分字段
labels={
'username':'用户名',
'email':'邮箱',
}
help_texts={
'username':'...'
}
widgets = {
'username':Fwidgets.Textarea(attrs={'class':'c1'})
}
error_message = {
'__all__':{
},
'email':{
'required':'邮箱不能为空',
'invalid':'邮箱格式错误'
}
}
field_classes = {
# 'email':Ffields.URLField
}
# localized_fields=('ctime',)
# 钩子验证同form
def clean_username(self):
old = self.cleaned_data['username']
return old
class UserInfoForm(forms.Form):
username = Ffields.CharField(max_length=32)
email =Ffields.EmailField()
user_type =Ffields.ChoiceField(
choices=models.UserType.objects.values_list('id','caption')
)
def __init__(self,*args,**kwargs):
super(UserInfoForm,self).__init__(*args,**kwargs)
self.fields['user_type'].choices=models.UserType.objects.values_list('id','caption')
def index(request):
# form 代码
# if request.method=='GET':
# obj =UserInfoForm()
# return render(request,'index.html',{'obj':obj})
# elif request.method=="POST":
# obj =UserInfoForm(request.POST)
# obj.is_valid()
# obj.cleaned_data
# obj.errors
# # models.UserInfo.objects.create(**obj.cleaned_data)
# models.UserInfo.objects.filter(id=1).update(**obj.cleaned_data)
# return render(request,'index.html',{'obj':obj})
# modelform代码
if request.method=='GET':
obj = UserInfoModelForm ()
return render(request,'index.html',{'obj':obj})
elif request.method=="POST":
obj =UserInfoModelForm(request.POST)
# print(obj.is_valid())
# print(obj.cleaned_data)
# print(obj.errors.as_json)
if obj.is_valid():
# obj.save()
# 拆分上步骤
instance = obj.save(False)
instance.save() # 只保存当前类
obj.save_m2m()
return render(request,'index.html',{'obj':obj})
def user_list(request):
li = models.UserInfo.objects.all().select_related('user_type')
# li = models.UserInfo.objects.all().select_related('user_type','u2g')
# 不能选择u2g
return render(request,'user_list.html',{'li':li})
def user_edit(request,nid):
if request.method=='GET':
# 获取当前id用户信息,显示已存在数据
user_obj = models.UserInfo.objects.filter(id=nid).first()
mf = UserInfoModelForm(instance=user_obj)
return render(request,'user_edit.html',{'mf':mf,'nid':nid})
elif request.method =='POST':
user_obj = models.UserInfo.objects.filter(id=nid).first()
mf = UserInfoModelForm(request.POST,instance=user_obj)
if mf.is_valid():
mf.save()
else:
print(mf.errors.as_json())
return render(request,'user_edit.html',{'mf':mf,'nid':nid})
# index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form action="/index/" method="POST">
{% csrf_token %}
{{ obj.as_p}}
<input type="submit">
</form>
</body>
</html>
# user_list.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<ul>
{% for row in li %}
<li>{{ row.username }} - {{ row.user_type.caption }} - <a href="/edit-{{ row.id }}/">编辑</a></li>
{% endfor %}
</ul>
</body>
</html>
# user_edit.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form action="/edit-{{ nid }}/"method="POST">
{% csrf_token %}
{{ mf.as_p }}
<input type="submit">
</form>
</body>
</html>
- Ajax
- 参考博客:
http://www.cnblogs.com/wupeiqi/articles/5703697.html
- 原生ajax(XMLHttpRequest)
- jQuery
- 伪Ajax操作(iframe)
- 使用时机:
如果发送的是【普通数据】
1. jQuery
2. XMLHttpRequest
3. iframe
- 文件上传
- Form提交
- Ajax上传文件(xhr/jQuery)
- 使用时机:
如果发送的是【文件】
1. iframe
2. jQuery(FormData)
3. XMLHttpRequest(FormData)
- ajax 和 文件上传 代码
# urls.py
url(r'^ajax/$', views.ajax),
url(r'^ajax_json/$', views.ajax_json),
url(r'^upload/$', views.upload),
url(r'^upload_file/$', views.upload_file),
# view.py
def ajax(request):
return render(request,'ajax.html')
def ajax_json(request):
print(request.POST)
ret ={'status':True,'data':request.POST.get('username')}
import json
# return HttpResponse(json.dumps(ret),status=404,reason='Not Found')
return HttpResponse(json.dumps(ret))
def upload(request):
return render(request,'upload.html')
def upload_file(request):
username = request.POST.get('username')
fafafa = request.FILES.get('fafafa')
print(username,fafafa)
import os
img_path =os.path.join('static/imgs/',fafafa.name)
with open(img_path,'wb') as f:
for item in fafafa.chunks():
f.write(item)
ret ={'status':True,'data':img_path}
import json
return HttpResponse(json.dumps(ret))
# ajax.html
<body>
<input type="text">
<input type="button" value="Ajax1" onclick="Ajax1();">
<!--
ifram 演示
<input type="text" id="url">
<input type="button"value="发送iframe" onclick="iframeRequest();">
<iframe id= 'ifm' src="http://www.baidu.com" frameborder="0"></iframe>
-->
<form action="/ajax_json/"method="POST" target="ifm1">
<iframe id="ifm1" name="ifm1" ></iframe>
<input type="text"name="username">
<input type="text"name="email">
<input type="submit" value="Form提交" onclick="submitForm();">
</form>
<script src="/static/jquery-1.12.4.js"></script>
<script>
//兼容性设置
function getXHR(){
var xhr = null;
if (XMLHttpRequest){
xhr = new XMLHttpRequest();
}else{
xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
return xhr;
}
//原生 ajax get请求
/*
function Ajax1(){
var xhr = new XMLHttpRequest();
xhr.open('GET','/ajax_json/',true);
xhr.onreadystatechange = function () {
if (xhr.readyState ==4){
console.log(xhr.responseText);
var obj = JSON.parse(xhr.responseText);
console.log(obj);
}
};
xhr.setRequestHeader('k1','v1');
xhr.send('name=root;pwd=123');
};
*/
//原生 ajax post请求
function Ajax1(){
//未包含csrf_tonken
// POST发送需要加请求头
var xhr = getXHR();
//var xhr = new XMLHttpRequest();
xhr.open('POST','/ajax_json/',true);
xhr.onreadystatechange = function () {
if (xhr.readyState ==4){
console.log(xhr.responseText);
var obj = JSON.parse(xhr.responseText);
console.log(obj);
}
};
xhr.setRequestHeader('k1','v1');//csrf_token
xhr.setRequestHeader('Content_Type','application/x-www-form-urlencoded');
xhr.send('name=root;pwd=123');
}
/*
// jquery ajax,参数a1,a2 有一个同xhr
$.ajax({
success: function (arg, a1, a2) { }
})
*/
/*
iframe 演示
function iframeRequest(){
var url = $('#url').val();
$('#ifm').attr('src',url);
}
*/
function submitForm(){
$('#ifm1').load(function () {
var text = $('#ifm1').contents().find('body').text();
// console.log(text);
var obj =JSON.parse(text);
})
}
</script>
</body>
</html>
# upload.html
<head>
<meta charset="UTF-8">
<title></title>
<style>
.upload{
display: inline-block;
padding: 8px;
background-color:cornflowerblue;
position: absolute;
top:0;
bottom: 0;
right:0;
left:0;
z-index: 90;
}
.file{
width: 100px;height: 50px;
opacity: 0;
position: absolute;
top:0;
bottom: 0;
right:0;
left:0;
z-index: 100;
}
</style>
</head>
<body>
<div style="position: relative; width: 100px;height: 50px;">
<input class="file" type="file" id="fafafa" name="afafaf">
<a class="upload" >上传</a>
</div>
<input type="submit" value="提交XHR" onclick="xhrSubmit();">
<input type="submit" value="提交jquery" onclick="jqSubmit();">
<hr>
<form id="form1" action="/upload_file/"method="POST" enctype="multipart/form-data" target="ifm1">
<iframe id="ifm1" name="ifm1" style="display: none;"></iframe>
<input type="file"name="fafafa" onchange="changeUpload();">
{# <input type="submit" value="Form提交" onclick="ifmSubmit();">#}
</form>
<div id="preview"></div>
<script src="/static/jquery-1.12.4.js"></script>
<script>
function changeUpload(){
$('#ifm1').load(function () {
var text = $('#ifm1').contents().find('body').text();
var obj =JSON.parse(text);
$('#preview').empty();
var imgTag = document.createElement('img');
imgTag.src='/'+ obj.data;
$('#preview').append(imgTag);
});
$("#form1").submit();
}
function jqSubmit(){
var file_obj = document.getElementById('fafafa').files[0];
var xhr = new XMLHttpRequest();
var fd = new FormData();
fd.append('username','root');
fd.append('fafafa',file_obj);
$.ajax({
url:'/upload_file/',
type:'POST',
data:fd,
processData:false,
contentType:false,
success: function (arg, a1, a2) {
console.log(arg);
console.log(a1);
console.log(a2);
}
})
}
function xhrSubmit(){
var file_obj = document.getElementById('fafafa').files[0];
var xhr = new XMLHttpRequest();
var fd = new FormData();
fd.append('username','root');
fd.append('fafafa',file_obj);
xhr.open('POST','/upload_file/',true);
xhr.onreadystatechange = function () {
if (xhr.readyState ==4){
console.log(xhr.responseText);
var obj = JSON.parse(xhr.responseText);
console.log(obj);
}
};
xhr.send(fd);
}
function ifmSubmit(){
$('#ifm1').load(function () {
var text = $('#ifm1').contents().find('body').text();
{# console.log(text);#}
var obj =JSON.parse(text);
$('#preview').empty();
var imgTag = document.createElement('img');
imgTag.src='/'+ obj.data;
$('#preview').append(imgTag)
})
}
</script>
</body>
- JSONP
# pip3 install requests
import requests
request.get('http://www.baidu.com')
request.post('http://www.baidu.com')
# 由于浏览器具有同源策略会阻止Ajax请求
# 但不阻止<script src='...'></script>)
# 巧妙:
- 创建script标签
- src=远程地址
- 返回的数据必须是js格式
# 只能发GET请求
# 提供接口:
def jsonp(request):
func = request.GET.get('callback')
content = '%s(1000000)' % (func,)
return HttpResponse(content)
# jQuery请求:
$.ajax({
url: 'http://www.jxntv.cn/data/jmd-jxtv2.html',
type: 'POST',
dataType: 'jsonp',
jsonp: 'callback',
jsonpCallback: 'list'
})
# 参考:
python s12 day13 JavaScript、Dom和jQuery
https://www.cnblogs.com/wupeiqi/articles/5369773.html
# CORS:
跨站资源共享
返回数据设置响应头
参考资料:
Ajax全套 cors
https://www.cnblogs.com/wupeiqi/articles/5703697.html
# 代码:
# view.py
def req(request):
response = requests.get('http://weatherapi.market.xiaomi.com/wtr-v2/weather?cityId=101121301')
#print(response.content) # 字节
response.encoding = 'utf-8'
#print(response.text) # 字符串
return render(request, 'req.html',{'result': response.text})
# req.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>后台获取的结果</h1>
{{ result }}
<h1>js直接获取结果</h1>
<input type="button" value="获取数据" onclick="getContent();" />
<div id="container"></div>
<script src="/static/jquery-1.8.2.js"></script>
<script>
function getContent(){
/*
var xhr = new XMLHttpRequest();
xhr.open('GET','http://wupeiqi.com:8001/jsonp.html?k1=v1&k2=v2');
xhr.onreadystatechange = function(){
console.log(xhr.responseText);
};
xhr.send();
*/
/*
var tag = document.createElement('script');
tag.src = 'http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403';
document.head.appendChild(tag);
document.head.removeChild(tag);
*/
$.ajax({
url: 'http://www.jxntv.cn/data/jmd-jxtv2.html',
type: 'POST',
dataType: 'jsonp',
jsonp: 'callback',
jsonpCallback: 'list'
})
}
function list(arg){
console.log(arg);
}
</script>
</body>
</html>
- Ajax2
$.ajax({
url: '/host/',
type: "POST",
data: {'k': 'v', 'list': [1,2,3,4], 'k3': JSON.stringfy({'k1': 'v'}))} --> $('form对象').serilize(),
dataType: 'JSON',
traditional: true,
success: function(data){
// data是服务器端返回的字符串
var obj = JSON.parse(data);
location.reload() # 刷新
location.href = "某个地址" # 跳转
}
})
$.get(url='xx',data={})
$.getJson
$.post
# 建议:永远让服务器端返回一个字典
# return HttpResponse(json.dumps(字典))
- Cookie
1、获取Cookie:
request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
参数:
default: 默认值
salt: 加密盐
max_age: 后台控制过期时间
2、设置Cookie:
rep = 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协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
3、由于cookie保存在客户端的电脑上,所以,JavaScript和jquery也可以操作cookie。
<script src='/static/js/jquery.cookie.js'></script>
$.cookie("list_pager_num", 30,{ path: '/' });
response = HttpResponse(a)
response.set_cookie()
return response
# cokies 通过响应头返回
response = HttpResponse(a)
response['name'] = ’alex‘
# 自己写响应头
- 示例:
user_info ={
'dachengzi':{'pwd':'123123'},
'kangbazi':{'pwd':'456789'}
}
def login(request):
if request.method=='GET':
return render(request,'login.html')
if request.method=='POST':
u = request.POST.get('username')
p = request.POST.get('pwd')
dic = user_info.get(u)
if not dic:
return render(request,'login.html')
if dic['pwd']== p:
res = redirect('/index/')
res.set_cookie('username',u)
return res
else:
return render(request,'login.html')
def index(request):
v = request.COOKIES.get('username')
if not v:
return redirect('/login/')
return render(request,'index.html',{'current_user':v})
def cookie(request):
request.COOKIES
request.COOKIES['username']
request.COOKIES.get('username')
response =render(request,'index.html')
response =redirect('/index/')
# 设置cockie,关闭浏览器失效
response.set_cookie('key','value')
# 设置cockie,n秒后失效
response.set_cookie('username','value',max_age=10)
# 设置cockie,截止时间失效
import datetime
current_date = datetime.datetime.utcnow()
current_date = current_date + datetime.timedelta(seconds=10)
response.set_cookie('username','value',expires=current_date)
# 应用:每页显示多少数量
response.set_cookie('username','value',path='/')
return response
- 用户认证装饰器
FBV:
def auth(func):
def inner(reqeust,*args,**kwargs):
v = reqeust.COOKIES.get('username111')
if not v:
return redirect('/login/')
return func(reqeust, *args,**kwargs)
return inner
@auth
def index(reqeust):
# 获取当前已经登录的用户
v = reqeust.COOKIES.get('username111')
return render(reqeust,'index.html',{'current_user': v})
CBV:
def auth(func):
def inner(reqeust,*args,**kwargs):
v = reqeust.COOKIES.get('username111')
if not v:
return redirect('/login/')
return func(reqeust, *args,**kwargs)
return inner
from django import views
from django.utils.decorators import method_decorator
# 仅对get
class Order(views.View):
@method_decorator(auth)
def get(self,reqeust):
v = reqeust.COOKIES.get('username111')
return render(reqeust,'index.html',{'current_user': v})
def post(self,reqeust):
v = reqeust.COOKIES.get('username111')
return render(reqeust,'index.html',{'current_user': v})
# 通过dispatch 对全部
class Order(views.View):
@method_decorator(auth)
def dispatch(self, request, *args, **kwargs):
return super(Order,self).dispatch(request, *args, **kwargs)
def get(self,reqeust):
v = reqeust.COOKIES.get('username111')
return render(reqeust,'index.html',{'current_user': v})
def post(self,reqeust):
v = reqeust.COOKIES.get('username111')
return render(reqeust,'index.html',{'current_user': v})
# 对全部方法
@method_decorator(auth,name='dispatch')
class Order(views.View):
def get(self,reqeust):
v = reqeust.COOKIES.get('username111')
return render(reqeust,'index.html',{'current_user': v})
def post(self,reqeust):
v = reqeust.COOKIES.get('username111')
return render(reqeust,'index.html',{'current_user': v})
- Session
基于Cookie做用户验证时:敏感信息不适合放在cookie中
a. Session原理
Cookie是保存在用户浏览器端的键值对
Session是保存在服务器端的键值对
b. Cookie和Session对比
c. Session配置(缺少cache)
d. 示例:实现两周自动登陆
- request.session.set_expiry(60*10)
- SESSION_SAVE_EVERY_REQUEST = True
PS: cookie中不设置超时时间,则表示关闭浏览器自动清除
- session依赖于cookie
- 服务器session
request.session.get()
request.session[x] = x
request.session.clear()
- 配置文件中设置默认操作(通用配置):
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过期(默认)
# set_cookie('k',123)
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
- 引擎的配置
详见别人博客
- 扩展:Session用户验证
def login(func):
def wrap(request, *args, **kwargs):
# 如果未登陆,跳转到指定页面
if request.path == '/test/':
return redirect('http://www.baidu.com')
return func(request, *args, **kwargs)
return wrap
- session用户登陆及CSRF相关示例
# urls.py
url(r'^login/$', views.login),
url(r'^index/', views.index),
url(r'^logout/', views.logout),
# views.py
from django.shortcuts import render,redirect,HttpResponse
# Create your views here.
def login(request):
if request.method =='GET':
return render(request,'login.html')
elif request.method=='POST':
user =request.POST.get('user')
pwd =request.POST.get('pwd')
if user=='root' and pwd =='123':
# 生成随机字符串,写到用户cookie,保存到session
# 随机字符串中 设置相关内容
# makemigrations, migrate
request.session['username'] = user
request.session['is_login'] = True
if request.POST.get('rmb')=='1':
request.session.set_expiry(10)
return redirect('/index/')
else:
return render(request,'login.html')
# from django.views.decorators.csrf import csrf_exempt,csrf_protect
# @csrf_exempt
# @csrf_protect
# 局部设置
def index(request):
# 获取当前用户字符串,并获取对应信息
if request.session.get('is_login',None):
return render(request,'index.html')
else:
return HttpResponse('go back')
# index.html
def logout(request):
request.session.clear()
return redirect('/login/')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>welcome:{{ request.session.username }}</h1>
<a href="/logout/">leave</a>
</body>
</html>
# login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form action="/login/"method="POST">
{% csrf_token %}
<input type="text" name="user">
<input type="text" name="pwd">
<input type="checkbox"name="rmb"value="1">10秒免登陆
<input type="submit">
<input id="btn" type="button"value="A提交">
<script src="/static/jquery-1.12.4.js"></script>
<script src="/static/jquery.cookie.js"></script>
<script>
$(function () {
$.ajaxSetup({
// 全局配置,对所有ajax配置请求头,携带csrf-token
beforeSend:function (xhr,settings) {
xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken'));
}
});
{# var csrf_tonken = $.cookie('csrftoken');#}
// get等请求时不携带x-csrftoken,需要在此做出判断,具体见其他。
$('#btn').click(function () {
$.ajax({
url:'/login/',
type:'POST',
{# headers:{'X-CSRFtoken': $.cookie('csrftoken')},#}
data:{'user':'root','pwd':'123'},
success: function (arg) {
}
})
});
})
</script>
</form>
</body>
</html>
- CSRF
a. CSRF原理
b. 无CSRF时存在隐患
# django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。
# 而对于django中设置防跨站请求伪造功能有分为全局和局部。
全局:
中间件 django.middleware.csrf.CsrfViewMiddleware
局部:
@csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
@csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
# from django.views.decorators.csrf import csrf_exempt,csrf_protect
c. Form提交(CSRF)
d. Ajax提交(CSRF)
CSRF请求头 X-CSRFToken
- 应用
1、普通表单
veiw中设置返回值:
return render_to_response('Account/Login.html',data,context_instance=RequestContext(request))
或者
return render(request, 'xxx.html', data)
html中设置Token:
{% csrf_token %}
2、Ajax
# 对于传统的form,可以通过表单的方式将token再次发送到服务端,而对于ajax的话,使用如下方式。
view.py
from django.template.context import RequestContext
# Create your views here.
def test(request):
if request.method == 'POST':
print request.POST
return HttpResponse('ok')
return render_to_response('app01/test.html',context_instance=RequestContext(request))
text.html
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
{% csrf_token %}
<input type="button" onclick="Do();" value="Do it"/>
<script src="/static/plugin/jquery/jquery-1.8.0.js"></script>
<script src="/static/plugin/jquery/jquery.cookie.js"></script>
<script type="text/javascript">
var csrftoken = $.cookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
function Do(){
$.ajax({
url:"/app01/test/",
data:{id:1},
type:'POST',
success:function(data){
console.log(data);
}
});
}
</script>
</body>
</html>
3、更多:https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
- 中间件
# middleware,在django中,中间件其实就是一个类,
# 在请求到来和结束后,django会根据自己的规则在合适的时机执行中间件中相应的方法。
# 在django项目的settings模块中,有一个 MIDDLEWARE_CLASSES 变量,其中每一个元素就是一个中间件,如下图。
# 与mange.py在同一目录下的文件夹 wupeiqi/middleware下的auth.py文件中的Authentication类
# 中间件中可以定义四个方法,分别是:
process_request(self,request)
process_view(self, request, callback, callback_args, callback_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response)
# 以上方法的返回值可以是None和HttpResonse对象,
# 如果是None,则继续按照django定义的规则向下执行,
# 如果是HttpResonse对象,则直接将该对象返回给用户。
代码待添加
缓存
# 由于Django是动态网站,所有每次请求均会去数据进行相应的操作,
# 当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,
# 缓存将一个某个views的返回值保存至内存或者memcache中,
# 5分钟内再有人来访问时,则不再去执行view中的操作,
# 而是直接从内存或者Redis中之前缓存的内容拿到,并返回。
Django中提供了6种缓存方式:
开发调试
内存
文件
数据库
Memcache缓存(python-memcached模块)
Memcache缓存(pylibmc模块)
1、配置
a、开发调试
# 此为开始调试用,实际内部不做任何操作
# 配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
# 引擎
'TIMEOUT': 300,
# 缓存超时时间(默认300,None表示永不过期,0表示立即过期)
'OPTIONS':{
'MAX_ENTRIES': 300,
# 最大缓存个数(默认300)
'CULL_FREQUENCY': 3,
# 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
},
'KEY_PREFIX': '',
# 缓存key的前缀(默认空)
'VERSION': 1,
# 缓存key的版本(默认1)
'KEY_FUNCTION' 函数名
# 生成key的函数(默认函数会生成为:【前缀:版本:key】)
}
}
# 自定义key
def default_key_func(key, key_prefix, version):
"""
Default function to generate keys.
Constructs the key used by all other methods. By default it prepends
the `key_prefix'. KEY_FUNCTION can be used to specify an alternate
function with custom key making behavior.
"""
return '%s:%s:%s' % (key_prefix, version, key)
def get_key_func(key_func):
"""
Function to decide which key function to use.
Defaults to ``default_key_func``.
"""
if key_func is not None:
if callable(key_func):
return key_func
else:
return import_string(key_func)
return default_key_func
b、内存
# 此缓存将内容保存至内存的变量中
# 配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
# 注:其他配置同开发调试版本
c、文件
# 此缓存将内容保存至文件
# 配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
}
}
# 注:其他配置同开发调试版本
d、数据库
# 此缓存将内容保存至数据库
# 配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table', # 数据库表
}
}
# 注:执行创建表命令 python manage.py createcachetable
e、Memcache缓存(python-memcached模块)
# 此缓存使用python-memcached模块连接memcache
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': 'unix:/tmp/memcached.sock',
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]
}
}
f、Memcache缓存(pylibmc模块)
# 此缓存使用pylibmc模块连接memcache
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': '127.0.0.1:11211',
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': '/tmp/memcached.sock',
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]
}
}
g. Redis缓存(依赖:pip3 install django-redis)
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 100}
# "PASSWORD": "密码",
}
}
}
from django_redis import get_redis_connection
conn = get_redis_connection("default")
2、应用
a. 全站使用
使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,
则使用FetchFromCacheMiddleware获取内容并返回给用户,
当返回给用户之前,判断缓存中是否已经存在,
如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
# 其他中间件...
'django.middleware.cache.FetchFromCacheMiddleware',
]
CACHE_MIDDLEWARE_ALIAS = ""
CACHE_MIDDLEWARE_SECONDS = ""
CACHE_MIDDLEWARE_KEY_PREFIX = ""
b. 单独视图缓存
方式一:
from django.views.decorators.cache import cache_page
@cache_page(60 * 15)
def my_view(request):
...
方式二:
from django.views.decorators.cache import cache_page
urlpatterns = [
url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)),
]
c、局部视图使用
a. 引入TemplateTag
{% load cache %}
b. 使用缓存
{% cache 5000 缓存key %}
缓存内容
{% endcache %}
- 信号
Django中提供了“信号调度”,用于在框架执行操作时解耦。
通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者。
1、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 # 创建数据库连接时,自动触发
对于Django内置的信号,仅需注册指定信号,当程序执行相应操作时,自动触发注册函数:
project 下创建py文件
from django.core.signals import request_finished
from django.core.signals import request_started
from django.core.signals import got_request_exception
from django.db.models.signals import class_prepared
from django.db.models.signals import pre_init, post_init
from django.db.models.signals import pre_save, post_save
from django.db.models.signals import pre_delete, post_delete
from django.db.models.signals import m2m_changed
from django.db.models.signals import pre_migrate, post_migrate
from django.test.signals import setting_changed
from django.test.signals import template_rendered
from django.db.backends.signals import connection_created
def callback(sender, **kwargs):
print("xxoo_callback")
print(sender,kwargs)
xxoo.connect(callback)
# xxoo指上述导入的内容
# projet 下的 project __init__ 下导入 import 上述py文件
from django.core.signals import request_finished
from django.dispatch import receiver
@receiver(request_finished)
def my_callback(sender, **kwargs):
print("Request finished!")
2、自定义信号
a. 定义信号
# project 下创建py文件
import django.dispatch
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])
b. 注册信号
# project 下创建py文件
def callback(sender, **kwargs):
print("callback")
print(sender,kwargs)
pizza_done.connect(callback)
c. 触发信号
# views下使用
from 路径 import pizza_done
pizza_done.send(sender='seven',toppings=123, size=456)
由于内置信号的触发者已经集成到Django中,所以其会自动调用,而对于自定义信号则需要开发者在任意位置触发。
- 分页(自定义的分页)
- html
{{ page_str|safe }}
- views
from django.utils.safestring import mark_safe
mark_safe(page_str)
- 分页示例
- 分页示例
# utils\pagination.py
from django.utils.safestring import mark_safe
class Page:
def __init__(self, current_page, data_count, per_page_count=10, pager_num=9):
self.current_page = current_page
self.data_count = data_count
self.per_page_count = per_page_count
self.pager_num = pager_num
@property
def start(self):
return (self.current_page - 1) * self.per_page_count
@property
def end(self):
return self.current_page * self.per_page_count
@property
def total_count(self):
v, y = divmod(self.data_count, self.per_page_count)
if y:
v += 1
return v
def page_str(self, base_url):
page_list = []
flag_fir = 0
flag_end = 1
if self.total_count < self.pager_num:
flag_end = 0
start_index = 1
end_index = self.total_count + 1
else:
if self.current_page <= (self.pager_num + 1) / 2:
start_index = 1
end_index = self.pager_num + 1
else:
flag_fir = 1
start_index = self.current_page - (self.pager_num - 1) / 2
end_index = self.current_page + (self.pager_num + 1) / 2
if (self.current_page + (self.pager_num - 1) / 2) >= self.total_count:
flag_end = 0
end_index = self.total_count + 1
start_index = self.total_count - self.pager_num + 1
fir = '<a class="page" href="%s?p=%s">%s</a>' % (base_url,1,1)
if flag_fir:
page_list.append(fir)
if self.current_page == 1:
prev = '<a class="page" href="javascript:void(0);">上一页</a>'
else:
prev = '<a class="page" href="%s?p=%s">上一页</a>' % (base_url, self.current_page - 1,)
page_list.append(prev)
for i in range(int(start_index), int(end_index)):
if i == self.current_page:
temp = '<a class="page active" href="%s?p=%s">%s</a>' % (base_url, i, i)
else:
temp = '<a class="page" href="%s?p=%s">%s</a>' % (base_url, i, i)
page_list.append(temp)
if self.current_page == self.total_count:
nex = '<a class="page" href="javascript:void(0);">下一页</a>'
else:
nex = '<a class="page" href="%s?p=%s">下一页</a>' % (base_url, self.current_page + 1,)
page_list.append(nex)
end = '<a class="page" href="%s?p=%s">%s</a>' % (base_url,self.total_count,self.total_count)
if flag_end:
page_list.append(end)
jump = """
<input style="width:50px;height:20px;" type='text'/><a onclick='jumpTo(this, "%s?p=");'> GO</a>
<script>
function jumpTo(ths,base){
var val = ths.previousSibling.value;
if( /^\d+$/.test(val) && val < %s ){
location.href = base + val;
}
}
</script>
""" % (base_url,self.total_count,)
page_list.append(jump)
page_str = mark_safe("".join(page_list))
return page_str
# html(含基于cookies的跳转,插件:jquery-1.12.4.js jquery.cookie.js)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
.pagination .page{
display: inline-block;
padding: 5px;
margin: 5px;
}
.pagination .page.active{
background-color: cornflowerblue;
color: white;
}
</style>
</head>
<body>
<ul>
{% for item in li %}
<li>{{ item }}</li>
{% endfor %}
</ul>
<div>
<select id="ps" onchange="changePageSize(this)">
<option value="10">10</option>
<option value="30">30</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
</div>
<div class="pagination">
{{ page_str }}
</div>
<script src="/static/jquery-1.12.4.js"></script>
<script src="/static/jquery.cookie.js"></script>
<script>
$(function(){
var v = $.cookie('per_page_count');
$('#ps').val(v);
});
function changePageSize(ths){
var v = $(ths).val();
console.log(v);
$.cookie('per_page_count',v, {'path': "/user_list/"});
location.reload();
}
</script>
</body>
</html>
# urls.py
url(r'^user_list/', views.user_list),
# views.py
from utils import pagination
LIST = []
for i in range(499):
LIST.append(i)
def user_list(request):
current_page = request.GET.get('p', 1)
current_page = int(current_page)
val = request.COOKIES.get('per_page_count',10)
val = int(val)
page_obj = pagination.Page(current_page,len(LIST),val)
data = LIST[page_obj.start:page_obj.end]
page_str = page_obj.page_str("/user_list/")
return render(request, 'user_list.html', {'li': data,'page_str': page_str})
- 组合搜索
# model.py
from django.db import models
# Create your models here.
class Category(models.Model):
caption = models.CharField(max_length=16)
class ArticleType(models.Model):
caption = models.CharField(max_length=16)
class Article(models.Model):
title = models.CharField(max_length=32)
content = models.CharField(max_length=255)
category = models.ForeignKey(Category)
article_type = models.ForeignKey(ArticleType)
# type_choice=(
# (1,'Python'),
# (2,'Openstack'),
# (3,'Linux'),
# )
# article_type = models.IntegerField(choices=type_choice)
# 内存级别分类
# url.py
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^article-(?P<article_type_id>\d+)-(?P<category_id>\d+).html', views.article,name='article'),
]
# views.py
from django.shortcuts import render
from app01 import models
# Create your views here.
def article(request,*args,**kwargs):
# print(request.path_info)
# from django.urls import reverse
# url = reverse('article',kwargs={'article_type_id': '1', 'category_id': '1'})
# print(url)
print(kwargs)
# {'article_type_id': '1', 'category_id': '1'}
# url 中字段设置需与数据库中参数一致,方可如此传递**kwargs
condition ={}
for k,v in kwargs.items():
kwargs[k] = int(v)
if v == '0':
pass
else:
condition[k] = v
article_type_list= models.ArticleType.objects.all()
# article_type_list = models.Article.type_choice
# 内存级别分类时
category_list= models.Category.objects.all()
# c = {'article_type_id': '1', 'category_id': '1'}
result = models.Article.objects.filter(**condition)
return render(
request,
'article.html',
{
'result':result,
'article_type_list':article_type_list,
'category_list':category_list,
'arg_dict':kwargs
})
# article.html(含simple_tag)
{% load filter %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
.condition a{
display: inline-block;
padding: 3px 5px;
border: 1px solid #DDDDDD;
margin: 5px;
}
.condition a.active{
background-color: deepskyblue;
</style>
</head>
<body>
<h1>过滤条件</h1>
<div class="condition">
<div>
{% filter_all arg_dict 'article_type_id' %}
<!--
{% if arg_dict.article_type_id == 0 %}
<a class="active" href="/article-0-{{ arg_dict.category_id }}.html" >全部</a>
{% else %}
<a href="/article-0-{{ arg_dict.category_id }}.html" >全部</a>
{% endif %}
-->
{% filter_article_type article_type_list arg_dict %}
<!--
{% for row in article_type_list %}
{% if row.id == arg_dict.article_type_id %}
<a class="active" href="/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a>
{% else %}
<a href="/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a>
{% endif %}
{% endfor %}
-->
</div>
<div>
{% filter_all arg_dict 'category_id' %}
<!--
{% if arg_dict.category_id == 0 %}
<a class="active" href="/article-{{ arg_dict.article_type_id }}-0.html" >全部</a>
{% else %}
<a href="/article-{{ arg_dict.article_type_id }}-0.html" >全部</a>
{% endif %}
-->
{% for row in category_list %}
{% if row.id == arg_dict.category_id %}
<a class="active" href="/article-{{ arg_dict.article_type_id }}-{{ row.id }}.html" >{{ row.caption }}</a>
{% else %}
<a href="/article-{{ arg_dict.article_type_id }}-{{ row.id }}.html" >{{ row.caption }}</a>
{% endif %}
{% endfor %}
</div>
</div>
<h1>查询结果</h1>
<ul>
{% for row in result %}
<li>{{ row.id }} - {{ row.title }}</li>
{% endfor %}
</ul>
</body>
</html>
# app01/templatetags/filter.py
from django import template
from django.utils.safestring import mark_safe
register = template.Library()
@register.simple_tag
def filter_all(arg_dict,k):
"""
{% if arg_dict.article_type_id == 0 %}
<a class="active" href="/article-0-{{ arg_dict.category_id }}.html" >全部</a>
{% else %}
<a href="/article-0-{{ arg_dict.category_id }}.html" >全部</a>
{% endif %}
:param arg_dict:
:return:
"""
if k == 'article_type_id' :
n1 = arg_dict['article_type_id']
n2 = arg_dict['category_id']
if n1 == 0:
ret='<a class="active" href="/article-0-%s.html" >全部</a>' % n2
else:
ret='<a href="/article-0-%s.html" >全部</a>' % n2
else:
n1 = arg_dict['category_id']
n2 = arg_dict['article_type_id']
if n1 == 0:
ret='<a class="active" href="/article-%s-0.html" >全部</a>' % n2
else:
ret='<a href="article-%s-0.html" >全部</a>' % n2
return mark_safe(ret)
@register.simple_tag
def filter_article_type(article_type_list,arg_dict):
"""
{% for row in article_type_list %}
{% if row.id == arg_dict.article_type_id %}
<a class="active" href="/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a>
{% else %}
<a href="/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a>
{% endif %}
{% endfor %}
:return:
"""
ret =[]
for row in article_type_list:
if row.id == arg_dict['article_type_id']:
temp = '<a class="active" href="/article-%s-%s.html">%s</a>' %(row.id,arg_dict['category_id'],row.caption)
else:
temp = '<a href="/article-%s-%s.html">%s</a>' %(row.id,arg_dict['category_id'],row.caption)
ret.append(temp)
"""
if row[0] == arg_dict['article_type_id']:
temp = '<a class="active" href="/article-%s-%s.html">%s</a>' %(row[0],arg_dict['category_id'],row[1])
else:
temp = '<a href="/article-%s-%s.html">%s</a>' %(row[0],arg_dict['category_id'],row[1])
ret.append(temp)
# 内存级别分类
"""
return mark_safe(''.join(ret))