本質socket接受請求返回response
socket能夠封裝,python標準庫就有
關於python2與python3,在3中,unicode是字符串,需編碼成字節bytes
① b'fffff'
② bytes('ffff',encoding='utf8')
③ 'ffff'.encode('utf8')
對於不一樣的url後綴/xxx,需做不一樣處理,封裝成不一樣的def函數
進階:將URL放在字典裏, func = None
if current_url in URL_DICT;
func = URL_DICT[current_url]
if func;
return func()
else;
return ['404'.encode('utf-8')]
之後修改增添dict便可,這是必須的
進階2:URL_DICT = {
"/index/\d+":func,
}
利用正則表示一類的URL,而後在返回函數的地方正則匹配,較複雜javascript
能夠將response內容寫在一個html裏(專門的文件夾view),response時用handle函數從文件中拿
<div>內容</div>css
能夠將引用的處理函數寫在一個py文件(文件夾controller)裏,而後import文件
f = open('view/index.html',mode='rb')
data = f.read()
f.close()
return [data,]
能夠從數據庫中拿內容,寫一個文件夾model
data = data.replace(b'uuuu','內容'.encode('utf-8'))
MVC框架 model view controller
MTV框架 model template viewhtml
1.目錄結構:
mysite
- mysite文件夾 #對整個程序進行配置
-init.py
-settings.py #配置文件
-url.py #URL對應關係
-wsgi.py #django自己寫不了socket,wsgi是建立socket的規範,網上有現成的
按照wsgi封裝socket的模塊,django用python內置的wsgiref,之後用uwsgi
-manage.py #管理django程序
app目錄
-migrations文件夾 sqlalchemy經過類建立數據庫,不能命令刪除建立錯誤的列,
只能登錄數據庫刪除,再從類把那一行刪掉
#數據修改表結構記錄
-init.py #python2中標註這是屬於python的一個文件夾,才能導入成功
-admin.py #Django的後臺管理
-apps.py #配置當前app
-models.py #經過類建立數據庫結構
-tests.py #單元測試
-views.py #業務邏輯代碼
2.Django用戶登陸示例
①<label for="">指定相關聯的表單控件input
②display:inline-block:將對象呈遞爲內聯對象,可是對象的內容做爲塊對象呈遞。
旁邊的內聯對象會被呈遞在同一行內,容許空格。
③表單提交標籤<form action method> <p> <label> <input type="text"> type="submit"
<style>label { width ....}
④把css和js文件放在static文件夾,css在<head>頭部用<link rel="stylesheet" href=../>
rel 屬性是必須的,規定當前文檔與被連接文檔/資源之間的關係。
而js用<script src=....>在標籤的最後引入
⑤靜態文件要配置settings,不能走url訪問,否則會出現404
STATICFILES_DIRS = (
os.path.join(BASE_DIR,'static'),#記得加逗號
)
3.請求與響應:用戶登陸與前端交互
①form表單action提交到自身網頁時,需csrf,在settings中註釋掉MIDDLEWARE的csrf文件便可。
②views.py中的request包含了用戶請求的全部信息,post or get,user-agent等,request.POST獲取用戶提交的全部信息前端
def login(request):
error_msg = ''
#print(request.method) 用戶提交的方式
if request.method == "POST":
# user = request.POST['user'] 索引不存在會報錯
# pwd = request.POST['pwd']
user = request.POST.get('user',None) get方法默認等於None,就不會報錯
pwd = request.POST.get('pwd',None)
if user == 'root' and pwd == '123':
return redirect('http://www.baidu.com') 跳轉到URL,重定向用redirect
else:
error_msg = '用戶名或密碼錯誤'
return render(request,'login.html',{'error_msg':error_msg})
③urlpattern中網址最後和form表單的action網址要一致,
url(r'^login/',views.login)和action="/login/"
urlpattern中的網址沒有/時django默認會加上,這樣訪問login/也能訪問
1. action中沒有/,而urlpattern中有時,就會匹配不上
2. action中有/,而urlpattern中沒有時,也能匹配
④表格<table></table>
<tr>定義表格的行
<td>定義表格的單元格
<th>定義表格的表頭
⑤html模板中的循環
{% for row in user_list %} 循環時用{% %}還要有結尾endfor
<tr>
<td>{{ row.username }}</td> 取值時用{{}}
<td>{{ row.gender }}</td>
<td>{{ row.email }}</td>
</tr>
{% endfor %}
⑥定義全局變量,用於循環輸出用戶列表
def home(request):
if request.method == 'POST':
u = request.POST.get('username') 獲取<input name="uesrname">提交的數據
g = request.POST.get('gender')
e = request.POST.get('email')
temp = {'username': u, 'email': e, 'gender': g} 字典格式
USER_LIST.append(temp) 添加到字典中用於循環
return render(request,'home.html',{'user_list':USER_LIST})
問題:重啓後輸入數據清空,數據庫能夠解決。
4.Django路由介紹
請求=>urls.py=>views.py(從數據庫models和templates拿東西)=>瀏覽器
5.內容整理
①視圖函數;
def func(request):
#request.method 就算小寫,django轉大寫GET /POST
#Request.GET.get('nid',None) 獲取url參數 如127.0.0.1:8000/home?nid=123
#Request.POST.get('',None) 獲取提交的參數
#return HttpResponse('字符串')
#return render(request,'xx.html')
#return redirect('只能url')跳轉,返回這個url給用戶本身去訪問
#/ULR 如/login 前面的/代指本地的域名127.0.0.1
②模板渲染
{{ }} 取值
{% %} 循環
對於索引,1. 列表 list.0 list.1
2. 字典 dict.k1 dict.k2
③查看詳細:
<td>
</td>
<td>
<a href="/detail?nid={{ row.id }}">查看詳細</a> 具體信息的連接
</td>
而後在urlpattern中配置,GET到id後去數據庫取出,放到html中返回
request.GET.get('nid') filter或select後用於html循環輸出
④刪除數據
1.刪除按鈕 <a class="del" href="#" row-id="{{ row.id }}"></a> #表示不跳
2.jQuery $(.del).click(function(){
var row_id=$(this).attr('row-id') 獲取刪除的id
$('#nid').val(row_id) 賦值id給彈出的對話框
})
3.彈出對話框
<div>
<form action="/del_host" method="POST"> 跳轉del的url,調用刪除函數
<input style="display:none" id="nid" type="text" name="nid"/> 隱藏,用於存值
<p>
<input type="submit"/> 提交數據
<input type="button"/> 取消,設置display=none
</p>
</form>
</div>
4.提交到url再到視圖函數
del_host -> delete_host函數
def delete_host(request):
nid = request.POST.get("nid")
delete from tb where id = nid 刪除數據
return redirect('/home') 跳轉回homejava
總結:
請求聲明週期:
- 瀏覽器構造請求體、請求體,往服務器發送請求
- 服務器路由系統處理request,匹配視圖函數
- 匹配成功後,執行views函數 CBV FBV
- views函數中業務處理,操做數據庫(原生sql,ORM)等
- 響應內容 響應頭設置ret.set_cookie ret['']=''python
概要:路由、視圖、模板、ORM操做(對應原生sql語句的用法)
回顧:django請求週期:django服務器前端有個url路由系統,對應不一樣的視圖函數,發起請求時,
路由系統匹配觸發視圖函數,返回數據庫、靜態文件等與模板文件渲染好的字符串。
Django啓動流程:加載URL對應關係,讀取配置文件,啓動socket
1. 獲取多個數據以及文件上傳
①radio單選框 request.POST.get
<input type="radio" name="gender" value="1"/>
②checkbox複選框 request.POST.getlist('')
<input type="checkbox" name="favor" value="11"/>
<input type="checkbox" name="favor" value="22"/>
v = request.POST.getlist('favor')
③選擇
<select name="city"> #<select name="city" multiple>多選
<option value="sh">上海</option>
<option value="bj">北京</option>
<option value="tj">天津</option>
</select>
④文件上傳 form標籤屬性enctype="multipart/form-data"
1.<input type="file" name="fafafa" />
2.obj = request.FILES.get('fafafa') #request.POST.get('fafafa')只能拿到文件名
返回一個文件對象,<class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
html中form標籤要設置enctype="multipart/form-data",這樣就設定提交的文件放到file
而字符串放到POST裏
3.print(obj,type(obj),obj.name) name返回的是名字,type返回django的一個文件類的對象
obj輸出時會輸出文件名字,類比面向對象的def __str__(self)方法,輸出return obj.name值
4.保存文件
file_path = os.path.join('upload',obj.name) 保存到一個文件夾中
f=open(file_path,mode='wb') 打開文件
for i in obj.chunks(): chunks()是迭代器,yield獲取一點一點上傳的文件
f.write(i) 一點點保存
f.close()
2. Django的FBV與CBV
FBV:function base view url對應的視圖函數mysql
CBV: class base view
-> /index/ 調用函數
/index/ 調用類,執行類的指定方法
url(r'^home', views.Home.as_view()),固定寫法
在views.py中寫類
from django.views import View
class Home(View):
def get(self,request): 提交方法爲get時執行這個方法
print(request.method)
return render(request,'home.html')jquery
def post(self,request):
print(request.method)
return render(request, 'home.html')git
get or post 或者其餘方法,django靠的是內置的反射dispatch函數分析請求method找類的對應方法
經過自定義def dispatch(self,request,*args,**kwargs):
print('before') #自定義操做,相似裝飾器的功能
result = super(Home, self).dispatch(request, *args, **kwargs)執行原來的方法
print('after')
return HttpResponse('OK') #get or post返回的結果給這個函數,這個函數return給頁面,這裏沒用返回
擴展點:可把get和post方法都要的操做放在這裏,好比驗證是否登錄成功
擴展點總結:
a.一個函數內部根據選擇分發執行函數,返回結果。
b.執行super()執行原父類的方法,先後便可自定製操做
web
3. Django目標語言循環字典(對於列表,直接循環)
<ul>
{% for row in user_dict.keys %} 循環keys,還能夠.values和.items
{% for k,row in user_dict.items %}
<li>{{ row }}</li>
{% endfor %}
</ul>
4. 基於正則表達式的URL
詳情頁的方法:1.<a target="_blank" href="/detail/?nid={{ k }}">{{ row.name }}</a>
2.經過問號傳遞參數k,經過url匹配一個view後,
3.view函數中 nid = request.GET.get('nid')
detail_info = USER_DICT[nid]
return render(request,'detail.html',{'detail_info':detail_info})
4.detail.html中{{ detail_info.name }}
更好的方法:正則表達式
<a target="_blank" href="/detail-{{ k }}">{{ row.name }}</a>
url(r'^detail-(\d+).html', views.detail), 一類url對應一個函數
def detail(request,nid):傳匹配的參數到視圖中
# url(r'^detail-(?P<nid>\d+)-(?P<uid>\d+)',views.detail) 匹配形參分組
def detail(request,*args,**kwargs) *args指列表元組,**kwargs指字典
5. Django對應的路由名稱
url(r'^index/', views.index,name="index") name索引這個url
<form action="{% url 'index' %}" method="post">
注:#request.path_info 當前訪問url的後綴,寫在form中返回當前頁面
①url(r'^index/(\d+)', views.index,name="index"),
from django.urls import reverse 導入reverse
v = reverse('index',args=(90,)) reverse反轉生成url
print(v)
html中生成url的方法:{% url 'name' 1 2 %}
②url(r'^index/(?P<nid>\d+)', views.index,name="index"),
from django.urls import reverse
v = reverse('index',kwargs={'nid':90})
html中生成url的方法:{% url 'name' nid=1 uid=2 %}
6.Django路由分發
url(r'^cmbd/', include('app.urls')), include app下的url
訪問時,127.0.0.1/cmdb/login 便可分類訪問
7.建立基本類型和生成數據庫結構
ORM:建立一個類,生成數據庫表(數字、字符串、時間)
兩種:dbfor codeforst
第一種建立數據庫,根據結構生成類的語句
第二種建立類生成數據庫,根據類操做數據庫
class UserInfo(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=64)
而後在settings中配置app,這樣makemigrations才能找到該models
注:還能修改sqlites爲mysql,要修改默認模塊爲pymysql
__init__中import pymysql,pymysql.install_as_MySQLdb()
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':'dbname',
'USER': 'root',
'PASSWORD': 'xxx',
'HOST': '',
'PORT': '',
}
}
8. ORM操做
增:
①models.UserInfo.objects.create(username='root',password='123')
②dic = {'username':'eric','password':'123'}
models.UserInfo.objects.create(**dic)
③obj = models.UserInfo(username='alex',password='123')
obj.save() #原理是這個
查:
result = models.UserInfo.objects.all() result是django的Queryset,->列表[obj(xx,xx,xx),]
print(result)
result = models.UserInfo.objects.filter(username='root').first()
神奇的雙下劃線 __gt,能跨表,無限雙下劃線
obj_list = models.UserInfo.objects.all().values('id','username') 返回Queryset列表,每個元素是對應的字典
obj_list = models.UserInfo.objects.all().values_list('id','username')返回Queryset列表,每個元素是對應的元組
刪:
models.UserInfo.objects.filter(id=4).delete()
更新:
models.UserInfo.objects.all().update(password='999')
models.UserInfo.objects.filter(id=3).update(password='999')
9.基於ORM實現用戶登陸
u = request.POST.get('user')
p = request.POST.get('pwd')
obj = models.UserInfo.objects.filter(username=u,password=p)搜索看是否存在,返回Queryset列表
obj = models.UserInfo.objects.filter(username=u,password=p).first()返回對象
返回用戶管理頁面
功能: 1.左側導航欄:<a class="menu" href="/cmdb/user_info">用戶管理</a>
2.點擊後跳轉用戶列表:
user_list = models.UserInfo.objects.all()
{% for row in user_list %}
<li> <a href="/cmdb/user_detail_{{ row.id }}/">{{ row.username }}</a> </li>
{% endfor %}
3.點擊後跳轉用戶詳情頁
url(r'^user_detail_(?P<nid>\d+)/', views.user_detail),
def user_detail(request,nid):
obj = models.UserInfo.objects.filter(id=nid).first()#不存在會返回none
# models.UserInfo.objects.get(id=nid) #不存在會報錯
return render(request,'user_detail.html',{'obj':obj,})
<h1>用戶信息</h1>
<h4>{{ obj.name }}</h4>
<h4>{{ obj.id }}</h4>
<h4>{{ obj.password }}</h4>
10. 基於ORM實現用戶增刪改查
增:①.html中添加表單
<h3>添加用戶</h3>
<form action="/cmdb/user_info/" method="POST">
<input type="text" name="user" />
<input type="text" name="pwd" />
<input type="submit" value="添加" />
</form>
②. 點擊表單跳轉回用戶列表,但不是get而是提交post
elif request.method == "POST":
u = request.POST.get('user')
p = request.POST.get('pwd')
models.UserInfo.objects.create(username=u,password=p)
return redirect('/cmdb/user_info/')
刪:<a href="/cmdb/userdel-{{ row.id }}/">刪除</a>|
點擊後跳轉到
url(r'^userdel-(?P<nid>\d+)/', views.user_del),
models.UserInfo.objects.filter(id=nid).delete()
return redirect('/cmdb/user_info/')
編輯:
<a href="/cmdb/useredit-{{ row.id }}/">編輯</a>|
點擊後跳轉到編輯頁
url(r'^useredit-(?P<nid>\d+)/', views.user_edit),
obj = models.UserInfo.objects.filter(id=nid).first()
return render(request,'user_edit.html',{'obj':obj})
<form action="/cmdb/useredit-{{ obj.id }}/" method="post">
<input style="display: none" type="text" name="id" value="{{ obj.id }}"/> 隱藏id
<input type="text" name="username" value="{{ obj.username }}" />
<input type="text" name="password" value="{{ obj.password }}"/>
<input type="submit" value="提交" />
</form>
提交後,request.method爲post
elif request.method == "POST":
nid = request.POST.get('id')
u = request.POST.get('username')
p = request.POST.get('password')
models.UserInfo.objects.filter(id=nid).update(username=u,password=p)
return redirect('/cmdb/user_info/')
11. Django字段類型
①增長數據庫的列時,
email = models.CharField(max_length=64) makemigrations時要提供一個統一值
gender = models.CharField(max_length=64,null=True) 默認爲空
②EmailField CharField若是不使用admin後臺,效果都同樣
四大基本類型:字符串、數字、時間、二進制
每一個種類對應不少類型的field
AutoField 自增
uid = models.AutoField(primary_key=True)
12. ORM 參數
null 數據庫中字段是否能夠爲空
default 默認值
db_column 數據庫中字段的列名
db_tablespace
default 數據庫中字段的默認值
primary_key 數據庫中字段是否爲主鍵
db_index 數據庫中字段是否能夠創建索引
unique 數據庫中字段是否能夠創建惟一索引
unique_for_date 數據庫中字段【日期】部分是否能夠創建惟一索引
unique_for_month 數據庫中字段【月】部分是否能夠創建惟一索引
unique_for_year 數據庫中字段【年】部分是否能夠創建惟一索引
auto_now 建立時,自動生成,obj.save會更新這個,而.update不會更新
#obj = UserGroup.objects.filter(id=1).first()
#obj.save()
#obj = UserGroup.objects.filter(id=1).update(caption='CEO')
auto_now_add 建立時,自動生成,
verbose_name Admin中顯示的字段名稱
blank Admin中是否容許用戶輸入爲空
editable Admin中是否能夠編輯
help_text Admin中該字段的提示信息
choices Admin中顯示選擇框的內容,用不變更的數據放在內存中從而避免跨表操做
如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)
error_messages 自定義錯誤信息(字典類型),從而定製想要顯示的錯誤信息;
字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
如:{'null': "不能爲空.", 'invalid': '格式錯誤'}
validators 自定義錯誤驗證(列表類型),從而定製想要的驗證規則
13.ORM外鍵操做
user_group = models.ForeignKey("UserGroup",to_field='uid',default=1)
to_field表示關聯的字段,不指定時默認關聯主鍵
獲取時,{% for row in user_list %}會返回user_group,這是外鍵表UserGroup的對象封裝,包含全部列
{{ row.user_group.caption }}
14.基於ORM的外鍵實現增長用戶
添加操做:
models.UserInfo.objects.create(
...
user_group = models.UserGroup.objects.filter(id=1).first() 兩次數據庫操做
...
)
models.UserInfo.objects.create(
....
user_group_id = 1 更快速
....
)
html中添加用戶時,選擇用戶組:
group_list = models.UserGroup.objects.all()
return render(request,'user_info.html',{'user_list':user_list,'group_list':group_list})
<select name="group_id">
{% for item in group_list %}
<option value=""{{ item.uid }}>{{ item.caption }}</option>
{% endfor %}
</select>
選擇後,將uid經過form action傳遞post.get()
1.建立一對多結構
b = models.ForeignKey(to="Bussiness",to_field="id")
2.獲取單表單數據的三種方式
v1 = models.Bussiness.objects.all()
# 列表中是對象,模板中row.caption
v2 = models.Bussiness.objects.all().values('id','caption')
#列表中是字典,模板中row.caption
v3 = models.Bussiness.objects.all().values_list('id', 'caption')
# 列表中是元組,模板中row.1
models.Bussiness.objects.get(id=1) 獲取一個對象,不存在會報錯
models.Bussiness.objects.filter(id=1) 獲取的是QuerySet,加上.first()獲取的是對象
3.一對多跨表操做
<td>{{ row.b.caption }}</td> b封裝了外鍵表的一整行對象,訪問要跨表,b_id不用跨表
<td>{{ row.b.code }}</td>
4.一對多塊表操做的三種方式 (與單數據表的不一樣在於,涉及到外鍵表的對象獲取)
對於QuerySet列表包含對象
for 循環輸出 for row in v1:row.b.caption
v1[0].b.caption 不循環時,經過.進行跨表
①v1 = models.Host.objects.filter(nid__gt=0) 返回[hostobj(ip,obj())]
# for row in v1:
# print(row.nid,row.b.caption)
返回的是列表,包含對象,[hostobj(ip,obj())],
模板中引用時: {{ row.nid }}
{{ row.b.caption }} 會跨表
②v2 = models.Host.objects.filter(nid__gt=0).values('nid','hostname','b_id','b__caption')
# for row in v2:
# print(row['nid'],row['b_id'],row['b__caption'])
返回的是列表包含字典,已經跨好表
模板中引用時: {{ row.nid }}
{{ row.b__caption }} 不會跨表了
③v3 = models.Host.objects.filter(nid__gt=0).values_list('nid', 'hostname', 'b_id', 'b__caption')
# for row in v3:
# print(row[0],row[1],row[2])
返回的是列表包含元組,已經跨好表
模板中引用時: {{ row.0 }}
{{ row.1 }}
5.增長一對多數據示例
自定義序號:forloop.counter
數據庫前面的數據刪除時,序號不是從1開始了,而{{forloop.counter}}能夠1 2 3這樣
{{forloop.counter0}}能夠從0開始
{{forloop.revcounter}} 表示倒序
{{forloop.revcounter0}}
模態對話框:<div class="shade hide"></div> 遮罩層 z-index: 100;
<div class="add-model hide"> 對話層 z-index: 101;
而後在模態框中添加input框,提交後獲取post請求,更新數據庫,redirect
注:若是return render(request,'host.html')的話,因爲沒有傳參數,html無數據,
能夠redirect,請求方式爲get,這樣跳轉早已寫好的傳參數的views函數
6.初識Ajax以及內容整理
$('#ajax_submit').click(function () {
$.ajax({ #開始語句
url: "/test_ajax/", #提交到哪一個url
type: 'POST', #提交方式
data: {'k': 'v', 'list': [1,2,3,4], 'k3': JSON.stringfy({'k1': 'v'}))},$(form對象).serilize()
# 字典不能嵌套字典,能夠將字典stringfy傳過去
traditional:true, # 提交的data中value包括數組時要聲明
// dataType:'JSON', # 後臺發送的字符串自動轉換爲json對象,text\html不做處理,xml能轉就轉這格式
// data:$('#add_form').serialize(), # 封裝提交更方便
success: function (data) { # 提交成功後執行函數
// JSON.parse(data); # 將字符串轉爲對象,轉字符串是stringfy(),對應前端json.dumps\loads
# 聲明dataType:'JSON'後,就不用轉換了
if(data == 'ok'){
location.reload() # 從新加載
location.href = "某個地址" # 跳轉,只能在這跳轉,不能在視圖函數中redirect
}
else{
$('#error_msg').text(obj.error);# 賦值
}
},
error:function(){
#網絡異常,url錯誤等
}
});
})
此外還有:
$.get(url='',data={},success=)
$.post() #內部都是調用ajax方法
建議:永遠讓服務端返回一個字典
7.Django序列化與Ajax
經過Ajax發送數據,在返回時,只能返回字符串,故進行序列化
Django發送:
a.HttpResponse返回字符串時,無需序列化與反序列化
HttpResponse('ok')
b.返回render渲染模板後生成的字符串,也無需序列化與反序列化
user_list = models.UserInfo.objects.all()
return render(request,'get_data.html',{'user_list':user_list})
注:不能return redirect,ajax不受理;而render返回的是渲染好數據的字符串,不用再dumps和parse
c.返回一個普通的字典,定義一個字典用於返回,統一,判斷時修改其中的value
ret = {'status':True,'error':None,'data':{}}
return HttpResponse(json.dumps(ret)) #只能返回字符串,用dumps序列化爲形式字典
注:python中序列化與反序列化是json.dumps\loads(),JavaScript中是JSON.stringfy\parse()
d.返回的字典中value是對象的QuerySet時,須要先將對象序列化爲字符串,再加入字典中
from django.core import serializers
user_list = models.BookType.objects.all()
ret['data']= serializers.serialize("json", user_list) #前端須要JSON.parse()轉爲對象列表便可循環,生成標籤等
e.字典中value是字典的QuerySet時,list便可,將QuerySet變成python的list
user_list = models.UserInfo.objects.all().values('id','username')
return HttpResponse(json.dumps(ret))
g.字典中value是元組的QuerySet時,list便可,將QuerySet變成python的list
user_list = models.UserInfo.objects.all().values_list('id','username')
return HttpResponse(json.dumps(ret))
Ajax發送:
a.普通的字典
data: {'k': 'v'}
b.字典value有數組時,須要聲明
data: {'k': 'v'}, 'list': [1,2,3,4]}
traditional:true #聲明數組
c.字典value有字典時,須要序列化字典
data: {'k': 'v'}, 'list': [1,2,3,4], 'k3': JSON.stringfy({'k1': 'v'}))}
d.封裝普通表單數據
data: $(form對象).serilize()
f.表單數據有文件時,須要建立FormData對象並聲明
var form = new FormData();
form.append('avatar_img', file_obj);
form.append('csrfmiddlewaretoken','{{ csrf_token }}');
$.ajax({
url: '/avatar_upload.html',
type:'POST',
data: form,
processData: false, // tell jQuery not to process the data #聲明1
contentType: false, // tell jQuery not to set contentType #聲明2
success: function (arg) { #這裏處理回調數據
var obj = JSON.parse(arg);
$('#previewImg').attr('src','/' + obj.data);
}
})
8.編輯一對多示例
刪:form表單提交,post id過去
ajax悄悄提交過去而後location.reload
remove標籤
編輯: ① <a class="edit">編輯</a>
② $('.shade,.edit-model').removeClass('hide'); 移除標籤
③ 編輯框<input>,其中能夠設置該行的信息做爲默認填充值,
var bid = $(this).parent().parent().attr('bid'); 獲取默認的選擇值
var hid = $(this).parent().parent().attr('hid'); 獲取要修改數據庫進行filter的id
var host = $('#now_host').text(); text()與val()不一樣,一個js一個jquery
var ip = $('#now_ip').text(); 經過這個方法獲取表單的數據
var port = $('#now_port').text();
$('#edit_form').find('select').val(bid); 找到該select框,賦默認值,多選框時返回列表
$('#edit_form').find('input[name="hid"]').val(hid); 找到name爲hid的框,輸入值,用於
下一步數據修改操做,否則ajax提交時
找不到點擊編輯前的ID
④把修改信息提交
data:$('#edit_form').serialize(),視圖函數接收後
models.Host.objects.update()便可完成編輯
添加:
$.ajax({
url: "/test_ajax/",
type: 'POST',
#data: {'hostname': $('#host').val(), 'ip': $('#ip').val(), 'port': $('#port').val(),'b_id': $('#sel').val()},#}
data:$('#add_form').serialize(), 封裝提交更方便
success: function (data) {
}
}
});
9.建立多對多以及增長示例
方式1:自定義
class Host(models.Model):
nid = models.AutoField(primary_key=True)
hostname = models.CharField(max_length=32,db_index=True)
ip = models.GenericIPAddressField(protocol="ipv4",db_index=True)
port = models.IntegerField()
b = models.ForeignKey(to="Bussiness",to_field="id")
class Application(models.Model):
name = models.CharField(max_length=32)
class HostToApp(models.Model):
hobj = models.ForeignKey('Host',to_field='nid')
aobj = models.ForeignKey('Application')
方式2
class Application(models.Model):
name = models.CharField(max_length=32)
r = models.ManyToManyField("Host")
沒法經過類進行數據庫修改
obj = models.Application.objects.get(id=1) 1表示application_id
obj.name
# 第三張自動生成的表進行操做
obj.r.add(2) 只能這樣經過r操做 2表示host_id
obj.r.add(2,3,4)
obj.r.add(*[1,2,3,4])
obj.r.remove(2,4) 相似
obj.r.clear 清除application_id=1的
obj.r.set([3,5,7]) 清除全部再保存3,5,7
====全部相關的主機對象「列表」=======
obj.r.all() 返回Queryset,裏面的對象是Host object
r能夠obj.r.get/all/filter,與objects同樣
增長: ①寫好模態框
②主機列表,因爲r是包含多個對象的列表,兩層循環
{% for app in app_list %}
<tr>
<td>{{ app.name }}</td>
<td>
{% for host in app.r.all %}
<span class="host-tag">
{{ host.hostname }}
</span>
{% endfor %}
</td>
</tr>
{% endfor %}
③輸入框,獲取Host主機名稱進行選擇
for op in host_list --->(models.Host.objects.all())
④post提交,form提交---->post跳轉後obj.r.add(*host_list)便可 getlist獲取多個選擇
ajax提交---->$.ajax({
url:'/ajax_app/',
data:$('#add_form').serialize(),
type:'POST',
dataType:'JSON', #不用再轉字符串爲對象
traditional:true, #可以傳輸多個主機選擇,即列表[1,2,3]
success:function (obj) {
if(obj.status){
location.reload()
}else{
alert('請輸入內容')
}
}
})
⑤視圖函數中接受,再更新數據庫
app_name = request.POST.get('app_name')
host_list = request.POST.getlist('host_list')
obj = models.Application.objects.create(name=app_name)
obj.r.add(*host_list)
編輯: ==========l相似添加,重點在於多對多的值怎麼獲取========
val獲取的是單選的值,一個數字或者字符串,設計多選時,拿到列表,也能夠傳一個列表給val
1.編輯框:
<select name="host_list" multiple>
{% for op in host_list %}
<option value="{{ op.nid }}">{{ op.hostname }}</option>
{% endfor %}
</select>
2.默認值
$('.edit').click(function () {
$('.edit-model,.shade').removeClass('hide');
var hid_list = [];
($(this)).parent().prev().children().each(function () { #應用對應的主機在多個span裏
var hid = $(this).attr('hid'); #獲取默認值,主機id
hid_list.push(hid) #增長
});
$('#edit_form').find('select').val(hid_list); #賦予默認值
3.獲取修改值
data:$('#edit_form').serialize(), # 其中的host_list是一個多選,traditional=true
4.數據庫更新
obj.r.set([list])
知識點
URL
-
Views
- 請求的其餘信息
- 裝飾器
Templates
- 母版...html
- 自定義函數
分頁(自定義的分頁)
cookie
session :裝飾器
Models
- 一大波操做
Form驗證
①URL 默認值
url(r'^index/', views.index,{'name':'root'})
def index(request,name):
URL命名空間:
url(r'^a/',include('app01.urls',namespace='author')),
url(r'^index/', views.index,name="index"),
在視圖函數中使用: reverse('author:index')
②兩種提交方式
Form表單提交:
提交 -> url > 函數或類中的方法
- ....
HttpResponse('....')
render(request,'index.html')
redirect('/index/')
用戶 < < 返回字符串
(當接受到redirect時)自動發起另一個請求
--> url .....
Ajax:
$.ajax({
url: '/index/',
data: {'k': 'v', 'list': [1,2,3,4], 'k3': JSON.stringfy({'k1': 'v'}))}, $(form對象).serilize()
# 字典不能嵌套字典,只能序列化
type: 'POST',
dataType: 'JSON':
traditional: true,
success:function(d){
location.reload() # 刷新
location.href = "某個地址" # 跳轉,只能在這跳轉,不能在視圖函數中redirect
}
})
提交 -> url -> 函數或類中的方法
HttpResponse('{}') 能返回內容更多
render(request, 'index.html', {'name': 'v1'}) 返回模板語言
如:<h1>{{ name }}</h1> --> <h1>v1</h1>
XXXXXXX redirect... 前端拿不到數據
用戶 <<<<< 字符串
③視圖函數
def func(request):
request.POST
request.GET
request.FILES
request.xxxx.getlist #獲取多個值
request.body 包含post、file和其餘發送方式發送的數據,而get發送的數據體如今url上
request.method 用於判斷請求方法
request.path_info 請求網頁
return render,HttpResponse,redirect
④模板
render(request, 'index.html')
# for
# if
# 索引. keys values items all(全部數據)
{{ item.m.all }} #模板語言中,執行方法不用加括號。
⑤models數據庫表
class User(models.Model):
username = models.CharField(max_length=32)
email = models.EmailField()
有驗證功能
Django Admin
無驗證功能:
User.objects.create(username='root',email='asdfasdfasdfasdf')
User.objects.filter(id=1).update(email='666')
1).一對多
class UserType(models.Model):
name = models.CharField(max_length=32)
class User(models.Model):
username = models.CharField(max_length=32)
email = models.EmailField()
user_type = models.ForeignKey("UserType")
獲取數據庫數據
方式一
user_list = User.objects.all() 列表
for obj user_list:
obj.username,obj.email,obj.user_type_id,obj.user_type.name,obj.user_type.id
方式二
user = User.objects.get(id=1) 一個對象
user.
方式三:
User.objects.all().values("username","user_type__name",)
2).多對多
class UserType(models.Model):
name = models.CharField(max_length=32)
class User(models.Model):
username = models.CharField(max_length=32)
email = models.EmailField()
user_type = models.ForeignKey("UserType")
m = models.ManyToMany('UserGroup') #也能放置UserGroup表中,也能夠第三張表ForeignKey兩表
class UserGroup(models.Model):
name = ....
obj = User.objects.get(id=1) #一個User對象
obj.m.add(2) #多對多關係操做
obj.m.add(2,3)
obj.m.add(*[1,2,3]) #帶*號
obj.m.remove(...)
obj.m.clear()
obj.m.set([1,2,3,4,5]) #不帶*號
# 返回一個列表包含多個組,UserGroup對象
obj.m.all()
obj.m.filter(name='CTO') 返回一個對象
2.視圖獲取用戶請求相關信息以及請求頭
request.environ
request.environ['HTTP_USER_AGENT']
3.模板繼承
{% extends 'xxx.html' %} 只能一個,要寫在開頭
{% block block_name %} 相似的還有:{% block js %}{% block css %}等
{% endblock %}
4.模板導入
{% include 'tag.html' %} 能夠導入多個,不要寫在開頭
5.自定義simple_tag(任意參數,隨意加空格,不能做if條件)
a. app下建立templatetags目錄
b. 任意py文件,寫一個func
c. 建立template對象register register = template.Library()
d. @register.simple_tag #裝飾
def func(a1,a2) #參數能夠多個
e. html中{% load xxoo %}
f. {% 函數名 arg1 arg2 %}
6.自定義filter(只能兩個參數,不能加空格,能做if條件)
@register.filter
def kelvin(a1,a2)
return a1+a2
{{ 'czl'|kelvin:'ls' }} {{ 參數一|函數名:參數二 }} 能夠傳數字,並接時str()便可
優勢:能放在if裏面做條件
{% if 'czl'|kelvin:'ls' %}
{% endif %}
7.自定義分頁
瀏覽器默認防護XSS攻擊,全部的字符串直接顯示,不做html格式編譯,須要管道符聲明一下
後臺:mark_safe(page_str)
前端:{{ page_str|safe }}
all_count = len(LIST)
每頁10個,總多少頁:count,y = divmod(all_count,10)
if y:
count += 1
page_list = []
for i in range(1,1+count): #一共多少頁
if i == current_page:
temp = '<a class="page active" href="/user_list/?p=%s">%s</a>'%(i,i)
else:
temp = '<a class="page" href="/user_list/?p=%s">%s</a>'%(i,i)
page_list.append(temp)
page_str = ''.join(page_list) 將列表拼接出來
page_str = mark_safe(page_str) 轉化爲安全的了
而後在前端便可:{{ page_str }}便可顯示代碼
函數化自定義功能(上一頁、下一頁、跳轉)
用到的幾個函數:int()
prev ='<a class="page" href="javascript:void(0)">上一頁</a>' 這是不跳轉,不跳轉還能用#表示
next = '<a class="page" href="/user_list/?p=%s">下一頁</a>' % (current_page + 1)
jump = """
<input type="text" /><a onclick='jumpto(this);'>GO</a> this表示當前點擊的標籤
<script>
function jumpto(ths){
var val = ths.previousSibling.value; 獲取點擊的<a>的上一個標籤text
location.href = "/user_list/?p=" + val; 串接url
}
</script> #這個script還能放在html中,定義好功能
"""
Django內置分頁
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
有兩個類Paginator,字段
# per_page: 每頁顯示條目數量
# count: 數據總個數
# num_pages:總頁數
# page_range:總頁數的索引範圍,如: (1,10),(1,200)
# page: page對象
以及Page類
# has_next 是否有下一頁
# next_page_number 下一頁頁碼
# has_previous 是否有上一頁
# previous_page_number 上一頁頁碼
# object_list 分頁以後的數據列表
# number 當前頁
# paginator paginator對象
使用:paginator = Paginator(L, 10) #L是總數據,10是每頁個數
posts = paginator.page(current_page)#post數據包含已切片數據,當前頁等
return render(request, 'index.html', {'posts': posts})便可
Django內置分頁擴展
class CustomPaginator(Paginator): #自定義一個類,繼承Paginator類,便可擴展
def __init__(self,current_page,per_pager_num,*args,**kwargs):
self.current_page = int(current_page)
self.per_pager_num = int(per_pager_num)
super(CustomPaginator,self).__init__(*args,**kwargs) #這樣初始化設置不會改變,方法不會受到影響
def pager_num_range(self):
#這裏能用繼承的類中init方法定義的self類字段,num_pages,per_pager_num,current_page三個參數
return range() #返回頁碼數的範圍,前端循環輸出每個頁碼便可
函數封裝
幾個要點:
①將用到的變量添加到init中
def __init__(self,current_page,all_count,per_page_count=10,pager_num=7):
這樣下面的函數引用時self.就行
②@property,放置在要不加括號的函數前面,使得函數self.total_count引用時不用加括號()
③將能提取的都提取出來,這樣就作成一個模板,使用時include傳參數便可
分頁具體使用:
def user_list(request):
current_page = request.GET.get('p',1) #獲取模板使用的參數
current_page = int(current_page) #轉換整數
page_obj = paginations.Page(current_page,len(li)) #導入這個文件,傳兩個參數便可
data=li[page_obj.start:page_obj.end] #不用加括號了
page_str = page_obj.page_str("/user_list/") #傳url參數
return render(request,'user_list.html',{'li':data,'page_str':page_str})
8.cookie
客戶端瀏覽器上的一個文件,發送請求時帶着(瀏覽器自動帶)
字典形式{'username':'asdf'}
登錄時:
res = redirect('/index/')
res.ser_cookie('username',u) 設置cookie的一個屬性爲username,值爲u
對應index視圖函數中
v = request.COOKIES.get('username') 獲取這個屬性看有沒有值
if not v:
return redirect('/login/') #沒有cookie就會轉到登錄頁面
return render(request,'index.html',{'current_user':v})
9.基於cookie實現用戶登錄
request.COOKIES
request.COOKIES['user']
request.COOKIES.get('')
response = redirect 返回內容
response = render
response.set_cookie('key','value') 要返回的時候設置cookie,放在響應頭裏
return response
key, 鍵
value='', 值
max_age=None, 超時時間
expires=None, 超時時間(IE requires expires, so set it if hasn't been already.)
import datatime #導入模塊
current_data = datatime.datetime.utcnow() #當前時間
current_data = current_data + datatiem.timedelta(seconds=5) #延遲
path='/', Cookie生效的路徑,/ 表示根路徑,特殊的:跟路徑的cookie能夠被任何url的頁面訪問
domain=None, Cookie生效的域名
secure=False, https傳輸
httponly=False 只能http協議傳輸,沒法被JavaScript獲取(不是絕對,底層抓包能夠獲取到也能夠被覆蓋)
10.基於cookie實現定製顯示數據條數
function changePageSize(ths) {
var v = $(ths).val() #記得傳進this
$.cookie('per_page_count',v) jQuery的cookie插件,使得可以這樣設置cookie值
location.reload()
}
注:jQuery插件獲取cookie值:$.cookie('key')
jQuery插件設置cookie值:$.cookie('key','v',{'path':''})
DOM設置cookie值:document.cookie="username='asdas'"
DOM獲取cookie值:document.cookie 返回全部
比較麻煩,還有設置超時時間等參數
後臺:
val = request.COOKIES.get('per_page_count') 視圖中獲取cookie賦值給函數
11.帶簽名的cookie
obj.set_signed_cookie('','',salt='')
request.get_signed_cookie('username',salt='')
12.CBV和FBV用戶認證裝飾器
CBV:
#裝飾器
def auth(func):
def inner(request,*args,**kwargs): #裝飾器須要一個inner函數
v = request.COOKIES.get('username')
if not v:
return redirect('/login/')
return func(request,*args,**kwargs)
return inner
@auth #在要應用的函數前面加上便可
FBV:
from django.utils.decorators import method_decorator
# @method_decorator(auth,name='dispatch') #方法三,name指明應用的函數,不用寫dispatch方法
class Order(views.View):
@method_decorator(auth) #方法二,對get和post都加上
def dispatch(self, request, *args, **kwargs):
return super(Order, self).dispatch(request, *args, **kwargs)
# @method_decorator(auth) #方法一,在單個函數前應用
def get(self,request):
v = request.COOKIES.get('username')
# if not v:
# return redirect('/login/')
return render(request, 'index.html', {'user': v})
def post(self, request):
v = request.COOKIES.get('username')
return render(request, 'index.html', {'user': v})
- Session
- CSRF
- Model操做
- Form驗證(ModelForm)
- 中間件
- 緩存
- 信號
內容概要,複製過來的:
1. Session
基於Cookie作用戶驗證時:敏感信息不適合放在cookie中
a. Session原理
Cookie是保存在用戶瀏覽器端的鍵值對
Session是保存在服務器端的鍵值對 {asd:{'':'',}}
步驟: 生成隨機字符串,寫到用戶瀏覽器cookie,保存到session中,
而後在隨機字符串對應的字典中設置相關內容。
獲取時,獲取當前瀏覽器的隨機字符串,獲取字符串對應的信息,進行匹配
b. Cookie和Session對比
依賴於cookie,
c. Session配置(缺乏cache)
d. 示例:實現兩週自動登錄
request.session['']=''
- request.session.set_expiry(60*10)
- SESSION_SAVE_EVERY_REQUEST = True
PS: cookie中不設置超時時間,則表示關閉瀏覽器自動清除
- session依賴於cookie
- 服務器session
request.session['k1'] #不存在會報錯
request.session.get('k1',None)
request.session['k1'] = 123 #已存在值會覆蓋掉,不存在會建立
request.session.setdefault('k1',123) # 存在則不設置
del request.session['k1']
request.session.delete("session_key")
request.session.clear() 先獲取該用戶隨機字符串,再delete,如註銷
- 配置文件中設置默認操做(通用配置):
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,默認修改以後才保存(默認)
更新超時時間
- 引擎的配置
2. CSRF
a. CSRF原理
get請求時發送一段只能本身反解的字符串,下次post發送數據的時候要帶着來,檢查請求頭,不然不受理
b. 無CSRF時存在隱患
避免其餘網站提交數據過來
c. Form提交(CSRF)
{% csrf_token %},會生成隱藏的input標籤帶着字符串信息
d. Ajax提交(CSRF)
CSRF請求頭 X-CSRFToken 全局默認的settings中會更新seetings.py配置,其中csrf有默認設置,字段爲
HTTP_X_CSRFTOKEN='',HTTP_是django自動加的前綴,請求頭不能包含下劃線,
這是非法的,後臺拿不到數據,X-CSRFTOKEN,官網推薦爲X-CSRFToken
方式一:
$.ajax({
...
headers:{'X-CSRFToken':$.cookie('csrftoken')}
})
方式二:統一配置一次便可,發送前
$.ajaxSetup({
beforeSend:function(xhr,settings){ #xhr爲xmlhttprequest對象
xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken')) #設置csrf
}
})
3. Model操做
a. 字段類型 + 參數
b. 連表字段 + 參數
c. Meta
d. SQL操做:
- 基本增刪改查
- 進階操做
- 正反查詢
- 其餘操做
e. 驗證(弱)
4. Form操做
完成:
- 驗證用戶請求
- 生成HTML
(保留上一次提交的數據)
自定義:
- 類
- 字段(校驗)
- 插件(生成HTML)
初始化操做:
a. Form驗證用戶請求
b. Form生成HTML
c. Form字段詳細(自定義字段,Model...) + 插件
d. 自定義驗證(鉤子以及__all__)
e. 註冊示例:
用戶名、密碼、郵箱、手機號(RegexValidator或RegexField)、性別、愛好、城市
f. 初始化值
5. ModelForm
a. Model+Form功能集合
b. save
c. save + save_m2m
6. 中間件
7. 緩存
5種配置
3種應用:
全局
視圖函數
模板
8. 信號
- 內置信號
- 自定義
- 定義信號
- 出發信號
- 信號中註冊函數
============= 做業:xxxoo管理 =============
用戶驗證:session
新URL:Form驗證
中間件:IP過濾
信號:記錄操做
CSRF:
本週內容開始
一. Django的url和views
URL
namespace等
views:
request.body #封裝了post提交的全部數據
request.POST(Request.body) #源碼中POST也是從body拿數據,沒有request.PUT,只能本身從body提取
request.FILES(Request.body)
request.GET
request.XXX.getlist
request.Meta() #請求頭的數據
request.method(POST,GET,PUT)
request.path_info
request.COOKIES
return HttpResponse #支持字節、字符串 如bytes('中國')
return render
return redirect
response = HttpResponse
response[‘name’]='alex' #能夠本身寫響應頭,瀏覽器的響應頭能夠看到
response.set_cookie()
return response 放在響應頭裏,返回cookie
返回的內容: 返回內容 響應頭(如cookie)
二.Model 操做
各類操做語句:增刪改查
三。模板
繼承、tags、simple_tag和filter
四。session
保存在服務器端的鍵值對(數據庫、緩存、內存),依賴於cookie
session = {
asdasdasd:{....}, 每一個用戶一個字典,保存信息
asdasdasd:{....},
}
原理:生成隨機字符串,寫到用戶瀏覽器cookie,再保存到session中,在隨機字符串對應的字典設置內容。
獲取時,獲取session鍵值對便可。
生成session
request.session['username'] = u
request.session['is_login'] = True
獲取session
if request.session['is_login']:
===================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的隨機字符串
request.session.session_key
# 將全部Session失效日期小於當前日期的數據刪除
request.session.clear_expired()
# 檢查 用戶session的隨機字符串 在數據庫中是否
request.session.exists("session_key")
# 刪除當前用戶的全部Session數據
request.session.delete("session_key")
request.session.clear() 先獲取該用戶隨機字符串,再delete,如註銷
request.session.set_expiry(value)
* 若是value是個整數,session會在些秒數後失效。
* 若是value是個datatime或timedelta,session就會在這個時間後失效。
* 若是value是0,用戶關閉瀏覽器session就會失效。
* 若是value是None,session會依賴全局session失效策略。
============session的配置=======================
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默認)
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,默認修改以後才保存(默認)
# 在已登陸狀態訪問頁面中時,session會一直更新過時時間
能存放session的地方
數據庫(默認)
緩存
文件
緩存+數據庫
加密cookie
五。CSRF原理
get服務器給一個信息,post時沒有這個信息不能提交
放在在別的網站能向其餘網站post數據
在設置{% csrf_token %}的時候,post時會生成隱藏的標籤,而且cookie會生成相應的字符串,
form表單提交會提交這個數據
這樣ajax提交的時候,獲取這個值到請求頭,發過去便可
$('#btn').click(function () {
$.ajax({
url:'/login/',
type:"POST",
data:{'user':'root','pwd':'123'},
headers:{'X-CSRFtoken':$.cookie('csrftoken')}, #設置請求頭的csrftoken值
success:function (args) {
}
})
})
或者統一配置:
$.ajaxSetup({
beforeSend:function (xhr,settings){ #在發送前的設置,xhr是ajax語言的根本
xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken')) #設置csrf
}
})
例外狀況:裝飾器
@csrf_protect,爲當前函數強制設置防跨站請求僞造功能,即使settings中沒有設置全局中間件。
@csrf_exempt,取消當前函數防跨站請求僞造功能,即使settings中設置了全局中間件。
對於ajax請求中,統一配置的時候,若是get方式提交,不用設置csrf了的時候,能夠判斷type的類型,不做settings
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);
}
}
});
六。中間件
請求---> 中間件request--->url--->views-->
請求<---中間件response<---response <----
若是某一中間件不經過了,從該中間件開始返回response。
應用:黑名單過濾
①from django.utils.deprecation import MiddlewareMixin
②class Row(MiddlewareMixin):
def process_request(self,request):
print('') #若是該處不經過條件,從該處開始返回response
def process_view(self, request, callback, callback_args, callback_kwargs)
print()
callback指url匹配的視圖函數,args指參數,kwargs指帶name的參數
def process_response(self,request,response):
print('')
return response
def process_exception(self, request, exception):
if isinstance(exception,ValueError):
return HttpResponse()
在settings.py中'文件夾.文件名.Row'寫入中間件的路徑
process_request(self,request)
process_view(self, request, callback, callback_args, callback_kwargs) #執行完request後從頭執行
process_template_response(self,request,response) #若是view函數中返回的對象中包含render方法
process_exception(self, request, exception) 視圖函數執行出錯時執行,從末尾開始,若是都沒有會報錯
process_response(self, request, response)
如 class Foo():
def render(self):
return HttpResponse():
def test(request):
return Foo() 就會調用process_template_response方法
有process_view版
請求---> 中間件request(順序)--->url(路由匹配)--->獲取views函數以及參數-->
請求<---中間件response(倒序)<---拿到視圖response <----執行中間件view(順序)<--
有process_exception版
請求---> 中間件request(順序)--->url(路由匹配)--->獲取views函數以及參數-->
請求<---倒序process_exception執行<---視圖函數出錯<----執行中間件view(順序)<--
有process_template_response版
請求---> 中間件request(順序)--->url(路由匹配)--->獲取views函數以及參數-->
請求<---中間件response(倒序)<---拿到視圖response<--執行process_template_response<--執行中間件view(順序)<--
七. 緩存
位置:數據庫、文件、內存,適合一些不常變更的數據,超時再從新拿一份放緩存
緩存方式:key-value,生成key的方式:
'KEY_PREFIX': '', # 緩存key的前綴(默認空)
'VERSION': 1, # 緩存key的版本(默認1)
'KEY_FUNCTION' 函數名 # 生成key的函數(默認函數會生成爲:【前綴:版本:key】)
如生成一個key爲p1:1:c1
開發調試(什麼都不作)
內存
文件
數據庫
Memcache緩存(python-memcached模塊)
Memcache緩存(pylibmc模塊) #什麼模塊取決於使用的模塊是哪一個,多個機器時按(,1)(,2)分配
將key轉數字除3,餘數爲0放第一,1和2放第二個機器
開發調試配置:
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】),能夠修改:爲-
}
}
應用:三個級別
①視圖函數級別緩存
from django.views.decorators.cache import cache_page
@cache_page(60 * 15)
def my_view(request):
ctime = time.time()
return的內容就會先從緩存裏面找
②局部緩存,好比商品描述能夠緩存,商品個數就不緩存
a. 引入TemplateTag
{% load cache %}
b. 使用緩存
{% cache 5000 緩存key %} #5000是超時時間s,存在緩存位置的key
緩存內容
{% endcache %}
③全站緩存(全部的函數和請求)
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware', #這個中間件只有response,不處理request
# 其餘中間件...
'django.middleware.cache.FetchFromCacheMiddleware',#這個中間件只有request,走的時候無論事
]
FetchFromCacheMiddleware放在最後,經過其餘csrf認證後纔給,這個中間件只有request
UpdateCacheMiddleware放在第一,中間的其餘中間件可能會response額外內容,這個中間件只有response
流程: 使用中間件,通過一系列的認證等操做,
若是內容在緩存中存在,則使用FetchFromCacheMiddleware獲取內容並返回給用戶,
當緩存中不存在,視圖函數處理返回內容,
同時UpdateCacheMiddleware會將緩存保存至緩存,從而實現全站緩存
緩存順序:全站的中間件有就直接返回了 順序:全站>函數>模板
八. 信號
如何在每次保存數據先後做一個操做
方法一:在views函數前加裝飾器,但這樣只能統一做操做,不能細化
方法二:在save()方法裏面加裝飾器,違反了不能修改源碼的原則
方法三:django爲每步操做先後留了鉤子(位置),在建立數據先後也同樣
原理:執行操做的時候會觸發相應的信號,好比post_save,裏面註冊了好多函數,save時觸發信號,順序執行這些函數
咱們只要找到該信號,往裏面註冊自定義的函數便可。
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 # 建立數據庫鏈接時,自動觸發
步驟:
①導入from django.db.models.signals import pre_init, post_init等
②自定義函數
def callback(sender, **kwargs): sender:源碼發送的觸發的全部信號,能夠做相關操做
print("xxoo_callback")
print(sender,kwargs)
③xxoo.connect(callback) 加入到內置信號
④有多個時,順序執行 xxoo.connect(f1) xxoo.connect(f2)
⑤註冊.py文件在settings所在文件夾的init.py中import
⑥接下來,在建立數據庫對象的時候就能send觸發信號,觸發callback函數
自定義信號
a. 定義信號
import django.dispatch
pizza_done(信號名) = django.dispatch.Signal(providing_args=["toppings", "size"])
b. 註冊信號
def callback(sender, **kwargs):
print("callback")
print(sender,kwargs)
pizza_done.connect(callback)
c. 觸發信號
from 路徑 import pizza_done
pizza_done.send(sender='seven',toppings=123, size=456) #這樣就觸發了該信號
最後,本身觸發這個自定義信號,執行註冊函數,通常設置特定條件下觸發該信號
應用:如公司服務器特定條件下發短信,如今改微信的話,只需保持源碼不動,改一下callback函數
爲微信接口,實現特定條件下觸發信號,執行註冊函數。
要點:實現瞭解耦。
九. Form組件認證
功能: ①做輸入驗證 (字段自己只作驗證)(驗證沒寫,繼承自Form的baseform的is_valid方法)
②生成輸入框的同時,保留輸入的內容,
post方式訪問時傳了post數據,CharField內部的widgts生成html標籤。
提交的input框數據可能不少,但Form只驗證類中字段名與input框name相同的數據
步驟:類--字段(校驗)--插件(生成HTML)
from django import forms 先導入
class FM(forms.Form):
user = forms.CharField(error_messages={'required':'用戶名不能爲空'})
pwd = forms.CharField(min_length=6,max_length=12,error_messages={'required':'用戶名不能爲空','min_length':'長度不能小於6'})
email = forms.EmailField(error_messages={'invalid':'格式錯誤','required':'用戶名不能爲空'})
//user_type = fields.ChoiceField(choices=models.UserType.objects.values_list('id','caption')) #數據須要更新
user_type = fields.ChoiceField()
def __init__(self,*args,**kwargs):
# super會拷貝全部的靜態字段user等,賦值給self.fields
super(FM,self).__init__(*args,**kwargs)
self.fields['user_type'].choices = models.UserType.objects.values_list('id','caption')
#每次調用建立對象時更新,若是不init,在編譯FM類時,每一個字段已經寫到內存,數據已經加載完,調用時只調用編譯那會的數據
#這樣的話第一次頁面加載時,能夠不添加choices,在init裏寫,建立對象時被引用便可
def fm(request):
if request.method == 'GET':
obj = FM() #傳一個空對象進去,這時的input框沒有值
return render(request,'fm.html',{'obj':obj}) #傳值
elif request.method == 'POST':
obj = FM(request.POST) #把post數據傳進去,這時的input框有輸入的值
r = obj.is_valid() #驗證數據是否成功
if r:
# print(obj.cleaned_data) #成功信息
models.UserInf.objects.create(**obj.cleaned_data) #字典加**便可建立,要求Form的字段與models中相同
else:
# print(obj.errors) #ErrorDict對象,繼承了dict,<ul><li>user<ul><li>具體錯誤信息</li>的形式,
#obj.errors.as_json()是一個字典,每一個key對應字段,默認是as_ul()
{'key1':[{'message':'ddd','code':'required'},...],'key2':}
return render(request,'fm.html',{'obj':obj}) #Form提交方式,若是Ajax提交須要把obj.errors賦值給data
在HTML中:
<form novalidate> #聲明novalidate以後,obj.errors便可正常顯示,並且自動顯示在errors標籤中
<p>{{ obj.user }}{{ obj.errors.user.0 }}</p> #ErrorDict對象,經過字典方式去拿,拿到user對應的錯誤信息列表的第一個
<p>{{ obj.pwd }}{{ obj.errors.pwd.0 }}</p>
<p>{{ obj.email }}{{ obj.errors.email.0 }}</p>
</form>
自定製包含標籤:
{{ obj.as_p }} 生成標籤P包含輸入框,不建議這樣,不能自定製
{{ obj.as_ul }} 生成列表
<table>{{ obj.as_table }}</table> 生成table,須要先後加標籤
Form擴展--定製樣式:
from django.forms import widgets
from django.forms import fields #之後字段都在這個fields裏面了
user = fields.CharField( #用field,不用forms
error_messages={'required':'用戶名不能爲空'},
widget= widgets.Textarea(attrs={'class':'c1'}) #widgets是插件,包含全部的標籤
若是要附加屬性,在括號內加便可。不加樣式括號不用加
)
Field
required=True, 是否容許爲空
widget=None, HTML插件,Field經過__str__\format方法並接字符串來生成html標籤,widget修改input的type
label=None, 用於生成Label標籤或顯示內容 {{ obj.user.label }}
initial=None, 初始值
help_text='', 幫助信息(在標籤旁邊顯示)
error_messages=None, 錯誤信息 {'required': '不能爲空', 'invalid': '格式錯誤'}
show_hidden_initial=False, 是否在當前插件後面再加一個隱藏的且具備默認值的插件(可用於檢驗兩次輸入是否一致)
validators=[], 自定義驗證規則,如[RegexValidator(r'^[0-9]+$', '請輸入數字')]
localize=False, 是否支持本地化,如本地時間
disabled=False, 是否能夠編輯
label_suffix=None Label內容後綴
CharField(Field)
max_length=None, 最大長度
min_length=None, 最小長度
strip=True 是否移除用戶輸入空白
IntegerField(Field)
max_value=None, 最大值
min_value=None, 最小值
FloatField(IntegerField)
...
DecimalField(IntegerField)
max_value=None, 最大值
min_value=None, 最小值
max_digits=None, 總長度
decimal_places=None, 小數位長度
BaseTemporalField(Field)
input_formats=None 時間格式化
DateField(BaseTemporalField) 格式:2015-09-01
TimeField(BaseTemporalField) 格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
DurationField(Field) 時間間隔:%d %H:%M:%S.%f
...
RegexField(CharField) #相似CharField+validators
regex, 自定製正則表達式
max_length=None, 最大長度
min_length=None, 最小長度
error_message=None, 忽略,錯誤信息使用 error_messages={'invalid': '...'}
EmailField(CharField)
...
FileField(Field) #文件的chunks在cleaned_data字典裏,字段名f就是字典的key,value是文件對象
allow_empty_file=False 是否容許空文件 #與input type=file同樣,只不過多了內置驗證
ImageField(FileField)
...
注:須要PIL模塊,pip3 install Pillow
以上兩個字典使用時,須要注意兩點:
- form表單中 enctype="multipart/form-data"
- view函數中 obj = MyForm(request.POST, request.FILES)
URLField(Field)
...
BooleanField(Field)
...
NullBooleanField(BooleanField) #能夠爲空
...
****ChoiceField(Field) #至關於CharField(widget=widgets.RadioSelect(choices=[(),()]))
...
choices=(), 選項,如:choices = ((0,'上海'),(1,'北京'),),列表[]也能夠
required=True, 是否必填
widget=None, 插件,默認select插件
label=None, Label內容
initial=None, 初始值,數字
help_text='', 幫助提示
ModelChoiceField(ChoiceField)
... django.forms.models.ModelChoiceField
queryset, # 查詢數據庫中的數據 models.User.objects.all(),html會顯示object,__str__便可
empty_label="---------", # 默認空顯示內容
to_field_name=None, # HTML中value的值對應的字段,好比username,value就會顯示數據庫中對應的username
limit_choices_to=None # ModelForm中對queryset二次篩選
ModelMultipleChoiceField(ModelChoiceField)
... django.forms.models.ModelMultipleChoiceField
TypedChoiceField(ChoiceField)
coerce = lambda val: val 對選中的值進行一次轉換,默認ChoiceField返回字符串,這個能夠轉換lambda x:int(x)
empty_value= '' 空值的默認值
MultipleChoiceField(ChoiceField)
...
choices=[],
initial=[1,2,3] #多選
TypedMultipleChoiceField(MultipleChoiceField)
coerce = lambda val: val 對選中的每個值進行一次轉換
empty_value= '' 空值的默認值
ComboField(Field) #包含其餘field在括號裏
fields=() 使用多個驗證,以下:即驗證最大長度20,又驗證郵箱格式
fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
MultiValueField(Field)
PS: 抽象類,子類中能夠實現聚合多個字典去匹配一個值,要配合MultiWidget使用
SplitDateTimeField(MultiValueField) #使得一個字段能夠對應多個input框
input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
FilePathField(ChoiceField) 文件選項,目錄下文件顯示在頁面中,把一個路徑下全部文件列出來
path, 文件夾路徑
match=None, 正則匹配
recursive=False, 遞歸下面的文件夾
allow_files=True, 容許文件
allow_folders=False, 容許文件夾
required=True,
widget=None,
label=None,
initial=None,
help_text=''
GenericIPAddressField #與models中相同
protocol='both', both,ipv4,ipv6支持的IP格式
unpack_ipv4=False 解析ipv4地址,若是是::ffff:192.0.2.1時候,可解析爲192.0.2.1, PS:protocol必須爲both才能啓用
SlugField(CharField) 數字,字母,下劃線,減號(連字符)(只支持)
...
UUIDField(CharField) uuid類型
總結:單個值時,用CharField、IntegerField等,若是不知足,自定義正則表達式,
而選擇,無論單選仍是多選radio\select\checkbox,均可以用ChoiceField
經常使用選擇插件:
單radio,值爲字符串
user = fields.CharField(
initial=2,
widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),)) #CharField自己沒有choices,得用widget
)
單radio,值爲字符串
user = fields.ChoiceField(
choices=((1, '上海'), (2, '北京'),),
initial=2,
widget=widgets.RadioSelect
)
單select,值爲字符串
user = fields.CharField(
initial=2,
widget=widgets.Select(choices=[(1,'上海'),(2,'北京'),])
)
單select,值爲字符串
user = fields.ChoiceField(
choices=((1, '上海'), (2, '北京'),),
initial=2,
widget=widgets.Select
)
多選select,值爲列表 #多選只能choiceField,由於charField只容許字符串,單選一個是字符串
user = fields.MultipleChoiceField(
choices=((1,'上海'),(2,'北京'),),
initial=[1,],
widget=widgets.SelectMultiple
)
單checkbox
user = fields.CharField(
widget=widgets.CheckboxInput()
)
多選checkbox,值爲列表
user = fields.MultipleChoiceField(
initial=[2, ],
choices=((1, '上海'), (2, '北京'),),
widget=widgets.CheckboxSelectMultiple
)
初始化操做:
dic = {
'user':'root',
'pwd':'123123',
'email':'123',
'city':1,
'city2':[1,2],
}
obj = FM(initial=dic) #輸入字典,便可實現相似編輯時設置默認值的狀況
Form組件擴展:
1.簡單擴展
利用Form組件自帶的正則擴展
class MyForm(Form):
user = fields.CharField(
validators=[RegexValidator(r'^[0-9]+$', '請輸入數字'), RegexValidator(r'^159[0-9]+$', '數字必須以159開頭')],
)
或者c = fields.RegexField(regex=r'^[0-9]+$'),只能一個正則表達式
2.基於源碼擴展鉤子
a.源碼_clean_fields()中
for name, field in self.fields.items(): #循環每個要驗證的字段
... #驗證表達式,返回值給cleaned_data
if hasattr(self, 'clean_%s' % name): #判斷是否有clean_字段的方法
value = getattr(self, 'clean_%s' % name)() #執行clean_字段名()方法,須要返回值
self.cleaned_data[name] = value #賦值
定義的Form類中
def clean_username(self):
v = cleaned_data['username']
if models.UserInfo.objects.filter(username=v).count():
from django.core.exceptions import ValidationError
raise ValidationError('用戶名已存在') #會被上一級的try捕捉到
return v
b.源碼_clean_form()中
def _clean_form(self):
try:
cleaned_data = self.clean() #執行clean方法
except ValidationError as e: #捕捉異常
self.add_error(None, e) #None對應的ErrorDict的key爲__all__,總體錯誤信息
else:
if cleaned_data is not None:
self.cleaned_data = cleaned_data
自定義clean方法
def clean(self):
value_dict = self.cleaned_data
v1 = value_dict.get('username')
v2 = value_dict.get('user_id')
if v1 == ss and v2 == asd:
raise ValidationError('總體錯誤信息') #字典key是__all__,但前端不能obj.errors.__all__
return self.cleaned_data #不改變
注:總體錯誤信息在前端顯示爲{{ obj.non_field_errors }} 源碼中NON_FIELD_ERRORS = __all__
c.源碼_post_clean
默認pass,若是自定製須要捕捉異常,不然程序報錯
Model的一大波操做
- 建立數據庫表 多對多、一對多、one to one之間的關係以及參數
- 操做數據庫表
基本操做:.all .distinct .count
進階性能相關:select_related #跨表查詢時用到,把跨表數據全拿過來,後面.操做的時候再也不額外發sql查詢,
只能加連表的字段,這是一次查詢,放到內存中待用
models.xx.objects.all().select_related(f_key)
prefitch_related #多個表相連時用select_related性能會下降,它則是第一次查詢的時候進行屢次
查詢,先把A表全部數據拿到,再去B表條件查詢,id in A 表的f_key,好比
主機和業務線,可能全部的主機才2個業務線。
models.xx.objects.all().prefitch_related(f_key)
- 驗證功能:只有一個clean方法預留了鉤子
Form的一大波操做
-驗證:字段的is_valid方法,經過cleaned_data返回字典,errors返回錯誤信息
- Form提交:render傳一個對象obj,包含cleaned_data字典,errors即ErrorDict
- Ajax提交:驗證正確了cleaned_data是一個字典,能夠傳,但錯誤信息ErrorDict不是字典,是一個django對象,
不能用python的json.dumps序列化。
- 能夠as_json化爲字典,再as_json化爲字符串HttpResponse給瀏覽器
- 經過json.dumps定製一個Clis,進行特殊對象的序列化,什麼類型取什麼值,構造出json對象,返回須要的值
-預留鉤子:驗證數據是否符合正則表達式的同時,驗證其餘東西,好比用戶名是否已存在。
1.多個字段進行驗證時,先每個進行正則表達式驗證 + clean_字段名方法,這個方法能自定製函數,返回data
- 即執行is_valid()方法->errors()方法->full_clean()方法->_clean_fields()方法
這個方法會返回self.cleaned_data[name] = value 或者 self.add_error(name, e)
返回的數據從這個方法產生,而下面兩個方法是鉤子
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
class FInfo(forms.Form):
username = fields.CharField(max_length=5,
validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.', 'invalid')], )
email = fields.EmailField()
def clean_username(self): #格式:clean_字段名方法
#Form中字段中定義的格式匹配完以後,執行此方法進行驗證
value = self.cleaned_data['username']
if "666" in value: #自定製操做
raise ValidationError('666已經被玩爛了...', 'invalid')
return value #返回給cleaned_data[name]
2.再總體驗證,好比用戶名和密碼對不對,有clean(__all__)和_post_clean()兩個鉤子能夠定製總體的操做
- full_clean()方法->_clean_form()方法,
- 調用clean()方法,clean方法是個鉤子,能夠自定製須要的函數,返回cleaned_data
- except ValidationError as e:self.add_error(None, e)
- full_clean()方法->_post_clean()方法,
默認pass,這也是一個鉤子
流程:obj=FM(request.post)建立一個Form對象,先if obj.is_valid()觸發baseform內部的is_valid()方法,這個方法return會
調用errors方法,errors的return再調用full_clean()方法,建立和賦值全局變量,通過這個方法is_valid()return了值。
full_clean()方法挨個執行
- self._clean_fields() #賦值全局變量self.cleaned_data或者self.add_error,進行每一個字段的驗證
- self._clean_form() #鉤子,自定製的操做可能會從新賦值這兩個全局變量
- self._post_clean() #也是鉤子,默認pass
總結:is_valid()->每個字段進行正則+clean_字段名方法 ->clean(__all__)-> _post_clean()
不管Form提交仍是Ajax提交,都能驗證,只不過ajax提交的話From生成標籤的功能就沒意義了,並且Form提交的時候還能保留
上一次輸入的數據。
一. ModelForm
Model + Form 驗證+數據庫操做
1.缺點:耦合程度強,依賴於model裏面的類,適合小程序,如定製django的admin
優勢:model和form都要寫一遍字段,modelform直接利用model的字段
2.寫法:
class UserModelForm(forms.ModelForm):
class Meta:
model = models.User 經過User定位裏面的字段生成標籤,label名、錯誤信息等都是models定義的
fields = '__all__' 展現的字段,all表示所有,還能夠是['username'],還能exclude=['']
meta內的每一個字段是一行數據,兩行數據之間不要逗號
也能夠生成html標籤,視圖函數中驗證數據的方法和Form組件如出一轍
3.校驗功能:(與Form一致)
Form:UserForm --> Form --> BaseForm(is_valid)
ModelForm: UserModelForm --> ModelForm --> BaseModelForm-->BaseForm(is_valid) #與Form相同的方法
if request.method == 'GET':
obj = UserModelForm()
return render(request,'index.html',{'obj':obj})
elif request.method == 'POST':
obj = UserModelForm(request.POST) #錯誤時,顯示上一次已輸入值,這個和Form組件同樣
print(obj.is_valid()) #is_valid() 要加括號
print(obj.cleaned_data) #返回數據,usertype是一個對象
print(obj.errors)
return render(request,'index.html',{'obj':obj})
4.具體參數:
class Meta:
model, # 對應Model的
fields=None, # 字段
exclude=None, # 排除字段
labels=None, # 提示信息,一個字典
help_texts=None, # 幫助提示信息
widgets=None, # 自定義插件,須要導入forms的widgets as Fwidgets(別名),字典
error_messages=None, # 自定義錯誤信息,字典嵌套字典形式
error_messages={‘username’:{'required':'',}}
總體錯誤信息from django.core.exceptions import NON_FIELD_ERRORS
‘__all__’:{ } 定義總體
field_classes=None # 自定義字段類 (也能夠自定義字段)
from django.forms import fields as Ffields
field_classes = {'email':Ffields.URLField},fields與Meta中衝突
只能填類,加括號就是對象了,因此URLField沒有括號
localized_fields=('ctime',) # 本地化,如:這樣顯示的是本地東八區的時間
如:
數據庫中
2016-12-27 04:10:57
setting中的配置
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = True
則顯示:
2016-12-27 12:10:57
5.外鍵值的顯示:
model建立的表中,一對多時
user_type = models.ForeignKey(to='',to_field='')
直接用ModelForm建立標籤時,頁面選擇框顯示的是一個個UserType object對象
解決:在model中寫__str__
多對多相似,在建立標籤時,也是一個個UserGroup object
6.保存(生成html標籤其實沒省什麼功夫,要聲明meta,新增數據時最省)
if obj.is_valid():
obj.save() #保存對象和mangTomany
拆開對象和關係
instance = obj.save(False)
instance.save() #僅保存對象,instance指當前model對象
obj.save_m2m #保存manytomany
7.編輯例子:
li = models.UserInfo.objects.all().select_related('user_type')
先取出userinfo表數據,賦值給li
再取出與userinfo關聯的外鍵表的全部數據放到內存,不支持manytomany,括號中寫外鍵字段
{% for row in li %}
<li>{{ row.username }}-{{ row.user_type.caption }}-<a href=''>編輯</a></li>
{% endfor %}
點擊編輯跳轉新頁面:
獲取當前id對應的用戶信息
信息放到input框中
顯示默認輸入值
def edit(request,nid):
if request.method == 'GET':
user_obj = models.UserInfo.objects.filter(id=nid).first() #取出信息
mf = UserModelForm(instance=user_obj) #傳默認值
return render(request, 'edit.html', {'mf': mf,'nid':nid})
elif request.method == 'POST':
user_obj = models.UserInfo.objects.filter(id=nid).first()
mf = UserModelForm(request.POST,instance=user_obj) #instance用於更新,若是沒有,則新增
if mf.is_valid():
mf.save()
else:
print(mf.error.as_json())
return render(request, 'edit.html', {'mf': mf, 'nid': nid})
8.鉤子:(和Form如出一轍)
繼承來源同樣,鉤子也同樣。
is_valid裏面包含正則+clean_字段名方法,還有clean()和_post_clean()
def clean_username(self):
old = self.cleaned_data['username'] #拿到經過了定義的正則表達式驗證的數據
......
return old #必須返回cleaned_data,不然view拿不到
9.額外定義的字段
還能在class ModelForm自定義指定model外的字段,以Form的field形式,能夠用於免登錄等,記錄value值後用session和cookie便可
is_rmb = Ffields.CharField(
widget=Fwidgets.CheckboxInput()
)
class Meta:
model =
....
總結: 1.生成標籤:class meta定製
2.默認值,傳instance
3.額外的標籤
4.各類驗證 is_valid--先每一個字段的正則表達式,clean_字段名方法---clean---_post_clean()各類鉤子
5.保存,save,還能拆開,先save(false),獲取到對象instance、.save_m2m()
缺點:一鍵保存看出了耦合
2. Ajax
原生
jQuery #內部調用原生
僞Ajax操做
******XmlHttpRequest對象的主要方法:(原生ajax)
a. void open(String method,String url,Boolen async)
用於建立請求
參數:
method: 請求方式(字符串類型),如:POST、GET、DELETE...
url: 要請求的地址(字符串類型)
async: 是否異步(布爾類型) #異步發,不影響任何效果
b. void send(String body) #對比jQuery的data:
用於發送請求
參數:
body: 要發送的數據(字符串類型)
c. void setRequestHeader(String header,String value)
用於設置請求頭
參數:
header: 請求頭的key(字符串類型)
vlaue: 請求頭的value(字符串類型)
d. String getAllResponseHeaders()
獲取全部響應頭
返回值:
響應頭數據(字符串類型)
e. String getResponseHeader(String header)
獲取響應頭中指定header的值
參數:
header: 響應頭的key(字符串類型)
返回值:
響應頭中指定的header對應的值
f. void abort()
終止請求
主要屬性,如xhr.xxx來使用,不用加括號
a. Number readyState
狀態值(整數)
詳細:
0-未初始化,還沒有調用open()方法,只建立了xml對象;
1-啓動,調用了open()方法,未調用send()方法;
2-發送,已經調用了send()方法,未接收到響應;
3-接收,已經接收到部分響應數據;
4-完成,已經接收到所有響應數據;
b. Function onreadystatechange
當readyState的值改變時自動觸發執行其對應的函數(回調函數)
這是一個屬性,寫在open前也能夠,只要在建立xhr以後便可
c. String responseText
服務器返回的數據(字符串類型)
d. XmlDocument responseXML
服務器返回的數據(Xml對象)
e. Number states
狀態碼(整數),如:200、404... 如return HttpResponse(json.dumps(ret),status=404,reason='NOT FOUND')
f. String statesText
狀態文本(字符串),如:OK、NotFound...
示例:
function Ajax1() {
var xhr = getXHR(); #通常建立寫法var xhr = new XMLHttpRequest()
xhr.open('POST','/ajax_json/',true);
xhr.onreadystatechange = function(){ #狀態碼改變時自動執行
if (xhr.readyState == 4){ #狀態碼
var obj = JSON.parse(xhr.responseText); #轉化爲json對象,能夠理解爲字典
console.log(obj);
}
};
xhr.setRequestHeader('k1','v1'); #設置請求頭,如csrf
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8');
#POST必須按照格式設置文件解析請求頭,GET不用設置直接發
xhr.send('name=root;pwd=123'); #發送數據
瀏覽器兼容問題:
function getXHR(){
var xhr = null;
if(XMLHttpRequest){
xhr = new XMLHttpRequest();
}else{
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
return xhr;
}
僞Ajax:
利用iframe框改src時框跳轉,但大頁面不刷新,實現不刷新發送請求,而普通的form表單會刷新
<input type="text" id="url"/>
<input type="button" value="發送" onclick="ifm_request();"/>
<iframe id="ifm" src="http://www.baidu.com"></iframe> #跳轉網頁
function ifm_request() {
var url = $('#url').val();
// console.log(url)
$('#ifm').attr('src',url);
}
所以,利用iframe實現僞ajax方法,不依賴任何插件
<form action="/ajax_json/" method="post" target="ifm1"> #target屬性讓form和iframe創建關係,表單數據經過iframe發送
<iframe id='ifm1' name="ifm1"></iframe> #iframe框
<input type="text" name="username" />
<input type="text" name="email" />
<input type="submit" onclick="submitForm();" value="提交"/>
</form>
寫一個js,當點擊提交時,獲取發送的數據:
function submitForm() {
$('#ifm1').load(function () {
var text = $('#ifm1').contents().find('body').text(); #contents是整個html,找到body標籤
var obj = JSON.parse(text); #text是字符串,須要轉爲字典
})
}
iframe標籤包含的是document對象,至關於上下文,或者空間管理,嵌套了html,innerText、children等沒法使用
對象裏面有html標籤-header\body標籤,body標籤的文本即返回的字符串
.contents拿到html對象
form提交後,要等到服務器返回數據時iframe框才接收到數據,同時觸發onload事件,在jQuery爲$().load()
==========ajax操做時機=========================
若是發送的是普通的數據、字符串 ------> jQuery,XMLHttpRequest,iframe
若是發送的是文件 ------> iframe,jQuery(FormData),XMLHttpRequest(FormData)
3. 文件上傳
-Form上上傳
-ajax(3種) #悄悄上傳、圖片預覽
上傳標籤的樣式:定義一個按鈕,和input file標籤相同大小,file在上層,透明度=0便可
<style>
.upload{
display: inline-block;padding: 10px;
background-color: brown;
position: absolute;
top: 0;
right: 0;
left: 0;
z-index: 90;
}
.file{
display: inline-block;padding: 10px;
opacity: 0;
background-color: brown;
position: absolute;
top: 0;
right: 0;
left: 0;
z-index: 100;
}
</style>
<div style="position: relative;width: 100px;height: 50px;">
<input class="file" type="file" id="fafafa" name="fafafa" />
<a class="upload">上傳</a>
</div>
①原生xhr操做
function xhrSubmit() {
//$('#fafafa')[0] #$('')取得的是一個jQuery對象。而innerHTML是一個DOM屬性。
var obj = document.getElementById('fafafa').files[0]; #name相同的文件可能有多個,獲取第一個
var fd = new FormData(); #依賴這個,Form表單對象
fd.append('username','root'); #添加普通數據
fd.append('fafafa',obj); #添加文件數據也是能夠的
var xhr = new XMLHttpRequest();
xhr.open('POST','/upload_file/',true);
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
var obj = JSON.parse(xhr.responseText);
console.log(obj);
}
};
xhr.send(fd); #發送
}
視圖函數中
username = request.POST.get('username')
fafafa = request.FILES.get('fafafa') #獲取文件,可chunks()寫入,是一個對象,封裝了大小,名稱,內容等
import os
img_path = os.path.join('static/imgs/',fafafa.name)
print(username,fafafa) #打印的是文件名,__str__()
with open(img_path,'wb') as f:
for item in fafafa.chunks():
f.write(item)
ret = {'status':True,'error':None,'data':img_path} #返回文件路徑
return HttpResponse(json.dumps(ret))
②jQuery上傳
function jqSubmit() {
var obj = document.getElementById('fafafa').files[0];
var fd = new FormData(); #也依賴這個,低版本瀏覽器可能不兼容
fd.append('username','root');
fd.append('fafafa',obj);
$.ajax({ #不用xml語句了
url:'/upload_file/',
type:'POST',
data:fd,
processData: false, // tell jQuery not to process the data #上傳文件時,聲明,內部不用轉化字符串拼接
contentType: false, // tell jQuery not to set contentType
success:function (arg,a1,a2) {
console.log(arg);
console.log(a1);
console.log(a2);
}
})
}
視圖函數寫入chunks的寫法同樣
③僞ajax
<form action="/upload_file/" method="POST" enctype="multipart/form-data" target="ifm1"> #form聲明
<iframe id='ifm1' name="ifm1"></iframe>
<input type="file" name="fafafa"/>
<input type="submit" onclick="ifmSubmit();" value="提交"/>
</form>
用到的script,兼容性高,現網站廣泛使用這個,在全部網站都適用
如下代碼可做查看
function ifmSubmit() {
$('#ifm1').load(function () {
var text = $('#ifm1').contents().find('body').text(); #文件經過form的post傳到view保存了
var obj = JSON.parse(text); #將view返回的字符串反轉爲字典
console.log(obj);
})
}
4.圖片預覽功能:
function ifmSubmit() {
$('#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; #路徑要以/開頭,這是靜態路徑,添加到src便可查看
$('#preview').append(imgTag); #添加內容
})
}
還能在上傳時便可預覽上傳,不用點提交,綁定一個onchange函數便可
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);
});
$('#fm1').submit(); #先綁定事件,再提交submit,用js替代了按鈕
}
4. 圖片驗證碼
原理:字符串放在session中,建立一張圖片寫入字符串,保存到本地指定文件夾,打開並讀取顯示
步驟:訪問頁面login
-建立一個圖片並給用戶返回
-session存放驗證碼
POST校驗
img標籤:
- 靜態文件:
src能夠是固定的本地靜態文件路徑,
也能夠是url,對應一個view函數,而後返回HttpResponse(open('','rb').read())
- 隨機新生成的圖片:
由於src寫不了固定的路徑,只能是views打開內容給url,src=url
# 1. 建立一張圖片 #pip3 install Pillow
# 2. 在圖片中寫入隨機字符串 #依賴pillow和字體文件
# obj = object() #特殊的對象,包含圖片信息,即已經放到內存中的一個類
# 3. 將圖片寫入到指定文件
# 4. 打開指定目錄文件,讀取內容
# 5. HttpResponse(data) #返回圖片內容給img標籤,能顯示,而直接返回給一個頁面則亂碼
#靜態圖片在img標籤直接寫url,django默認打開圖片讀取內容並返回
stream = BytesIO() #字節io操做,在內存開闢文件空間,讀寫。不一樣瀏覽器請求時開闢的地方不一樣
驗證碼圖片不必保存在服務器上,頭像則要保存並且命名要不一樣
img,code = create_validate_code() #返回一個圖片對象和驗證碼
img.save(stream,'PNG') #寫上文件句柄和後綴,文件句柄也能夠是open的本地文件
request.session['Checkcode'] = code
return HttpResponse(stream.getvalue()) #拿到內存的圖片
若是要更新,src = src + ? #?發get請求,加一個問號無所謂
5.KindEditor 文本編輯框
基本使用
<textarea name="content" id="content"></textarea>
<script src="/static/jquery-1.12.4.js"></script>
<script src="/static/plugins/kind-editor/kindeditor-all.js"></script>
<script>
$(function () {
initKindEditor();
});
function initKindEditor() {
var kind = KindEditor.create('#content', {
width: '100%', // 文本框寬度(能夠百分比或像素)
height: '300px', // 文本框高度(只能像素)
minWidth: 200, // 最小寬度(數字)
minHeight: 400 // 最小高度(數字)
});
}
</script>
上傳文件:
function initKindEditor() {
var kind = KindEditor.create('#content', {
width: '100%', // 文本框寬度(能夠百分比或像素)
height: '300px', // 文本框高度(只能像素)
minWidth: 200, // 最小寬度(數字)
minHeight: 400, // 最小高度(數字)
uploadJson: '/kind/upload_img/', #文件提交的url,圖片、flash、文件、視頻都在提交這個url
extraFileUploadParams: {
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
fileManagerJson: '/kind/file_manager/', #文件管理路徑
allowPreviewEmoticons: true, #遠程
allowImageUpload: true #是否容許本地上傳
});
}
views返回的內容:
dic = {
'error': 0,
'url': '/static/imgs/20130809170025.png',
'message': '錯誤了...'
}
return HttpResponse(json.dumps(dic))
XSS攻擊
輸出文章的時候作不了,那就提交的時候過濾掉,寫一個白名單
獲取輸入框內容
kind=KindEditor.html()
2018/05/04 週五 s25
一. 博客系統
1.IntegerField替代ManyToManyField,ForeignKeyField
caption = ((1,'a'),(2,'b'),(3,'c'))
choice_id = models.IntegerField(choices = caption)
減少數據庫連表操做,適用於分類不變的,要想變化,得搞一張表
2.組合搜索----
①從數據庫拿數據列出文章和搜索條件
<ul>
{% for row in result %}
<li>{{ row.id }}-{{ row.title }}</li>
{% endfor %}
</ul>
經過正則表達式filter數據庫
經過url傳匹配的id到view函數,url的名字與model中定義的字段名相同,而後直接**kwargs來filter便可:
url(r'^article-(?P<article_type_id>\d+)-(?P<category_id>\d+).html',views.article),
result = models.Article.objects.filter(**condition) #傳入字典,等同於where...and...
若是等於0,返回所有(實現所有這個標籤功能)
for k,v in kwargs.items():
kwargs[k] = int(v) # 用於和row_id進行比較,因此要轉爲數字
if v == 0:
pass #表示不加條件,不做篩選,即所有
else:
condition[k] = v #不然加上
②給條件標籤<a>加上href,
方式一:
<a href="article-{{ row.id }}-{{ arg_dict.category_id }}.html"> {{ row.caption }}</a>
#arg_dict.category_id,傳進url正則匹配到的參數字典,便可獲取到另外一個條件是哪一個值(python的分割、reverse)
<a href="article-0-{{ arg_dict.category_id }}.html">所有</a>
#所有即表示0
方式二:經過反轉生成url
<a href="{% url "video2" direction_id=item.id classification_id=kwargs.classification_id level_id=kwargs.level_id %}" class="active">{{ item.name }}</a>
給選中標籤加顏色,經過條件判斷賦予一個class,而後css樣式便可
{% if row.id == arg_dict.article_type_id %} #表示選中
<a class="active" href="article-{{ row.id }}-{{ arg_dict.category_id }}.html"> {{ row.caption }}</a>
{% endif %}
一樣,給所有標籤也加顏色
{% if arg_dict.article_type_id == 0 %} #表示點擊所有時,url中匹配的該行id=0
③用simple_tag簡化html標籤
-建立app下templatetags目錄,建立py
-寫函數func實現相同功能
回傳標籤語言,mark_safe(),生成多個a標籤時,ret是個列表,須要化爲一個總體,''.join()
-註冊
register = template.Library() @register.simple_tag
-html引入{% load xxoo %}
-{% 函數名 arg1 arg2 %} 傳入參數,
注:在view函數中對象經過.操做,字典經過[],而HTML中對象和字典都是經過.
④固定的標籤能夠放在內存中,減小數據庫操做次數
type_choice = ( #元組嵌套元組的形式,第一個是id,第二個是caption
(1,'Python'),
(2,'OpenStack'),
(3,'Linux'),
)
article_type_id = models.IntegerField(choices=type_choice) #寫法,替代ForeignKey(),名稱對應外鍵字段_id
檢索時,與外鍵相同的外鍵字段_id去filter便可
注:與ForeignKey不一樣,這裏字段名是什麼url中匹配組就是什麼,這樣models filter時直接傳
views.py中
article_type_list = models.Article.type_choice #從數據庫中拿數據,type_choice是表的靜態字段
# 利用map方法和lambda表達式轉換元組爲列表嵌套字典
status_list = list(map(lambda x:{'id':x[0],'name':'x[1]'},models.Video.status_choice))
if row[0] == n1: #封裝函數中元組的取值方法
新增一個條件:url正則匹配,views列數據,前端html代碼增長。其餘條件filter等不變
3.多對多組合搜索條件
a.如何構成分類條件字典用於filter
若是:direction_id 0 #方向不影響分類
*列出全部的分類
若是 classification_id = 0: #分類不加入條件
pass
else:
condition['classification_id'] = classification_id
不然:direction_id != 0 #方向影響分類
*列表當前方向下的全部分類
若是 classification_id = 0: #該方向下的全部分類加入條件
獲取當前方向下的全部分類 [1,2,3,4]
condition['classification_id__in'] = [1,2,3,4]
else: #該方向下的某個分類加入條件
classification_id != 0
獲取當前方向下的全部分類 [1,2,3,4]
classification_id 是否在 [1,2,3,4] : #上一次點擊前的classification_id可能這一次不在該方向下
condition['classification_id'] = classification_id #在
else:
condition['classification_id__in'] = [1,2,3,4] #不在
b.代碼
direction_id = kwargs.get('direction_id') #獲取url傳的參數
classification_id = kwargs.get('classification_id')
level_id = kwargs.get('level_id')
direction_list = models.Direction.objects.all() #列出全部的方向
if direction_id == 0:
class_list = models.Classification.objects.all()#方向下的全部分類
if classification_id == 0:
pass #分類是所有,不加入條件
else:
condition['classification_id'] = classification_id
else:
direction_obj = models.Direction.objects.filter(id=direction_id).first()
class_list = direction_obj.classification.all() # 當前方向下的全部分類,用於前端循環列出
vlist = direction_obj.classification.all().values_list('id') #d當前方向下的全部分類id,queryset元組列表
if not vlist:
classification_id_list = [] #vlist分類id可能在該方向下沒有
else:
classification_id_list = list(zip(*vlist))[0] #當前方向下的全部分類id,元組形式
if classification_id == 0: #分類是所有,把該方向下的全部分類加入條件
condition['classification_id__in'] = classification_id_list
else:
if classification_id in classification_id_list: #判斷分類是否在方向下
condition['classification_id'] = classification_id
else:
#################指定方向:[1,2,3] 分類:5
kwargs['classification_id'] = 0
condition['classification_id__in'] = classification_id_list
if level_id == 0:
pass
else:
condition['level_id'] = level_id
level_list = models.Level.objects.all() #全部的水平
video_list = models.Video.objects.filter(**condition) #filter
c.前端html標籤
相似一對多,生成對應的href便可
<a href="{% url "video2" direction_id=0 classification_id=kwargs.classification_id level_id=kwargs.level_id %}" class="active">所有</a>
<a href="{% url "video2" direction_id=item.id classification_id=kwargs.classification_id level_id=kwargs.level_id %}" class="active">{{ item.name }}</a>
二. JSONP跨域請求本質
json是格式,jsonp是一種請求方式
用途:目前請求都在本身寫的project裏,請求往返發,尚未和外界交互,拿取數據。
往外界發數據時,請求發過去了,但本地瀏覽器阻止接收。
1.python模塊發請求:
目前爲止,能夠requests庫請求get一個網址(瀏覽器請求的url是指向內部視圖函數的,網站是視圖函數經過request庫
請求的,與瀏覽器無關,而跳過視圖函數就要在html中經過ajax發起請求)
如:
response = requests.get('http://weatherapi.market.xiaomi.com/wtr-v2/weather?cityId=101121301')
response封裝了響應頭、響應內容、cookie等字節信息,
response.content是字節類型的,
response.text是字符串,須要先response.encoding='utf-8'編碼
2.ajax發請求
不經過django的view函數發請求,而是頁面經過按鈕觸發JavaScript直接向外界請求
3.示例:原生ajax的xhr語法請求
function getContent() {
var xhr = new XMLHttpRequest(); #建立對象
xhr.open('GET','http://weatherapi.market.xiaomi.com/wtr-v2/weather?cityId=101121301');
xhr.onreadystatechange = function () {
console.log(xhr.responseText);
}
}
獲取失敗:瀏覽器同源策略,不容許到別的網站請求數據返回,阻止ajax(XMLHttpRequest)請求,但有些不阻止
4.巧妙:後門(iframe,a,img標籤),js的cdn網址,即script的src屬性=...?k1=v1在script標籤加載時,即經過get方法發送參數過去
var tag = document.createElement('script');
tag.src = 'http://weatherapi.market.xiaomi.com/wtr-v2/weather?cityId=101121301'; ?表式get方式
document.head.appendChild(tag); #添加src標籤時,即對網址發去了請求
document.head.removeChild(tag); #請求完成後去掉多餘標籤,js已經拿到數據放到內存
若是直接返回字符串'ok',則無變量接受,報錯。src屬性要給瀏覽器一個Javascript文件,如alert(111),以及function
5.原理:本質上src就是請求一個js文件,以JavaScript的格式進行解析。
經過JavaScript添加一個src=請求url的script標籤,而後刪除,便可實現發送請求。
即步驟4代碼
遠程能夠返回一個執行函數(js格式),會調用瀏覽器上定義好的同名函數
callback(10000)
而後在html上再定義:
function callback(arg) {
alert(arg)
}
這樣,在數據交互的時候,遵循JSONP格式,包裹函數名
6.問題點:這個函數可能與自我定義的其餘函數名字衝突
7.方法:在url再傳一個參數
tag.src = 'http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403'; #callback是通常命名規則
服務器端:經過 func = request.GET.get('callback')
content = 「%s(100000)」 %(func,)
return HttpResponse(content)
html上,經過function list(arg){}便可對返回的內容進一步操做
應用:好比電視臺給的一個url,list('節目列表')返回,本地html上function list(arg){
console.log(arg) #便可打印節目列表
}
總結:只能發get請求,$.ajax method雖然是post,但也是將data附在url上轉化爲get方式進行請求
8.jQuery已經封裝好功能:(方法同樣,加src標籤而後刪除)
$.ajax({
url: 'http://www.jxntv.cn/data/jmd-jxtv2.html',
type: 'GET', #JSONP只能GET,post也會內部改成get
dataType: 'jsonp', #以這個方式去發送請求
jsonp: 'callback', #?callback=func經過Ajax發送時不能放在url,得設置這兩個
jsonpCallback: 'list' #對應url的寫法,以callback=list的方式去請求
})
9.跨站資源共享CORS:
遠程服務器端設置一個響應頭指定哪一個url或者域名進行訪問,就能經過瀏覽器的同源策略阻止
obj = HttpResponse('sss')
obj['Access-Control-Allow-Origin']='*'
存在瀏覽器兼容性問題
三. XSS過濾
文本編輯框返回safe過的html標籤內容,在裏面寫script的話會形成alert等(html解析時從上到下解析,遇到script執行)
(直接alert的話< >會被轉義,不是尖括號,因此不是script標籤了)
解決:過濾掉特殊標籤
re模塊\beatifulsoup4模塊
from bs4 import BeautifulSoup
soup = BeautifulSoup(content, 'html.parser') #內置的解析器
tag = soup.find('script') #找到指定標籤
tag.clear() #清除內容,也能夠tag.hidden = True
span = soup.find('script')
del span.attrs['style'] #刪除span標籤的style屬性
content = soup.decode() #將對象轉化成字符串形式
或者寫一個白名單
for tag in soup.find_all(): #找到全部標籤
if tag.name in tags:
pass
else:
tag.hidden = True
tag.clear()
continue #跳出當前for循環,下面的屬性篩選代碼執行不到
若是標籤包含屬性class id等
tags = {
'p': ['class'],
'strong': ['id',]
}
input_attrs = tag.attrs # 標籤的全部屬性,一個字典{'class': 'c1', 'id': 'i1'}
valid_attrs = tags[tag.name] # 獲取白名單中的屬性字典,['class']
for k in list(input_attrs.keys()): # for k,v in xxx.items()行不通,緣由在於迭代過程就del了自己元素
#.keys()是迭代器,list()至關於不對原字典做迭代了
if k in valid_attrs:
pass
else:
del tag.attrs[k] # 刪除不在名單中的屬性
問題:建立這個過濾方法的對象,若是多個訪問,則會建立多個對象,而白名單是寫死的,內存只存在一個便可,否則浪費內存
解決:單例模式
class Foo(object):
instance = None #靜態字段,只屬於類,建立多個類示例時只建立一個
def __init__(self):
self.name = 'alex' #name已經寫死
@classmethod #裝飾器,類方法
def get_instance(cls):
if Foo.instance: #若是已經建立,不爲空
return Foo.instance
else:
Foo.instance = Foo()#若是爲空,新建一個
return Foo.instance
def process(self):
return '123'
obj1 = Foo.get_instance() #兩個獲取到的同樣
obj2 = Foo.get_instance() #若是是obj.Foo(),則不同
進階:(建立對象時不換方式)
class Foo(object):
instance = None
def __init__(self): #後執行
self.name = 'alex'
def __new__(cls, *args, **kwargs):
if Foo.instance:
return Foo.instance
else:
Foo.instance = object.__new__(cls, *args, **kwargs) #建立對象的本質方法,不寫默認是這個建立
return Foo.instance
obj1 = Foo() #建立時會先執行new方法
四.博客系統
-博客表結構:
- 文章信息:
標題、簡介
閱讀數:read_count=models.IntegerField(default=0) #四個字段用來存數量,具體的信息在下個表維護
評論數:comment_count=models.IntegerField(default=0) #每當有人評論時+1
點贊數:up_count=models.IntegerField(default=0)
踩數:down_count=models.IntegerField(default=0)
- 贊或踩:放在同一個表中維護,用於檢索用戶是否贊過或者踩過
文章:ForeignKey(to='Article')
贊或踩用戶:ForeignKey(to='UserInfo')
是否贊:models.BooleanField(verbose_name='是否贊')
- 樓層相互評論:
評論內容:(content)
評論的文章:ForeignKey(to='Article')
評論者:ForeignKey(to='UserInfo')
回覆評論:reply=models.ForeignKey(verbose_name='回覆評論',to='self',related_name='back',null=True)
self指外鍵是本身的nid,只能從本身已有的nid中拿,null=True時指不回覆,僅評論文章
-功能:原子性事務操做(不成功回滾)
from django.db import transaction
with transaction.atomic():
obj = models.Article.objects.create(**form.cleaned_data) #文章信息
models.ArticleDetail.objects.create(content=content, article=obj) #具體內容
tag_list = []
for tag_id in tags:
tag_id = int(tag_id) #轉爲數字
tag_list.append(models.Article2Tag(article_id=obj.nid, tag_id=tag_id))
models.Article2Tag.objects.bulk_create(tag_list) #bulk_create是批量建立對象,減小SQL查詢次數
-文章時間分類:
先把c_time格式成年月,再進行分組(sqllite)
date_list = models.Article.objects.raw(
'select nid, count(nid) as num,strftime("%Y-%m",create_time) as ctime from repository_article
group by strftime("%Y-%m",create_time)')
後面的strftime是把create_time格式爲年-月,再group by
前面的strftime是已經分組後,將要顯示的列名稱
注:mysql是date_format(create_time,"%Y-%m")
而後列出文章:
article_list = models.Article.objects.filter(blog=blog).extra(
where=['strftime("%%Y-%%m",create_time)=%s'], params=[val, ]).all()
#字符串格式化的時候要想輸出%,前面得加一個%,這樣交給數據庫解析語句時就不是佔位符
-文章點贊:
js發送數據:文章id,贊or踩,session獲取登錄的用戶id
文章點贊個數自+1:F或者Q
5、瀑布流
思路:將圖片放在固定數量的div下,div寬度必定,高度變化。難點在於將圖片從數據庫取出循環放在幾個div中。
方式一:自定義filter
{% for row in img_list %}
{% if forloop.counter|函數名:1 %}
<div>
{% elif forloop.counter|函數名:2 %}
{% elif forloop.counter|函數名:3 %}
{% elif forloop.counter|函數名:0 %}
{% endif %}
{% endif %}
方式二:JavaScript
$(function () {
initImg();
});
NID = 0; #全局變量,監聽滾輪修改值,再次獲取數據
function initImg() {
$.ajax({
url: '/get_imgs.html',
type: 'GET',
data:{nid:NID}, #傳參數,用於從數據庫取哪一部分的數據filter(id__gt=nid)
dataType: 'JSON',
success:function (arg) {
var img_list = arg.data;
$.each(img_list,function (index,v) { #對img_list進行循環,index是循環自帶索引,v是img_list的每個值
var eqv = index % 4; #當前循環的索引除以4的餘數
var tag = document.createElement('img');
tag.src = '/'+ v.src;
$('#container').children().eq(eqv).append(tag); #拿到第幾個孩子
})
}
})
}
改進一:監聽滾輪,滑到底部再發請求取數據
$(function () {
initImg();
});
NID = 0; #全局變量,監聽滾輪修改值,再次獲取數據
LASTPOSITION = 3 #這個值依據4個div和每次取7張圖片來定,第一次循環完index=2
function initImg() {
$.ajax({
url: '/get_imgs.html',
type: 'GET',
data:{nid:NID}, #傳參數,用於從數據庫取哪一部分的數據filter(id__gt=nid)
dataType: 'JSON',
success:function (arg) {
var img_list = arg.data;
$.each(img_list,function (index,v) { #對img_list進行循環,index是循環自帶索引,v是img_list的每個值
var eqv = (index+LASTPOSITION+1) % 4; #當前循環的索引除以4的餘數
var tag = document.createElement('img');
tag.src = '/'+ v.src;
$('#container').children().eq(eqv).append(tag); #拿到第幾個孩子
if(index+1==img_list.length){ #最後一個圖片時
NID = v.id; #把最後圖片的id賦值給NID,下次再從這開始取
LASTPOSITION = eqv;
}
})
}
})
}
function scrollEvent(){
$(window).scroll(function () {
// 何時到達最底部
var scrollTop = $(window).scrollTop(); #滾動條可滾動到頂的高度
var winHeight = $(window).height(); #瀏覽器窗口高度
var docHeight = $(document).height(); #文檔html的body高度
if (scrollTop + winHeight == docHeight) {
initImg();
}
})
}
改進二:全局變量可能存在衝突問題
$(function () {
var obj = new ScrollImg(); #建立對象
obj.fetchImg(); #this代指obj
obj.scrollEvent();
});
function ScrollImg() { #函數嵌套函數,至關於定義了一個類
this.NID = 0; #this代指obj,至關於類的self
this.LASTPOSITION = 3;
this.fetchImg = function () {
var that = this; #經過對象調用方法,this仍是代指obj
$.ajax({
url: '/get_imgs.html',
type: 'GET',
data: {nid: that.NID},
dataType: 'JSON',
success: function (arg) {
var img_list = arg.data;
$.each(img_list, function (index, v) {
var eqv = (index + that.LASTPOSITION + 1) % 4; #that代指obj
console.log(eqv);
var tag = document.createElement('img');
tag.src = '/' + v.src;
$('#container').children().eq(eqv).append(tag);
if (index + 1 == img_list.length) {
that.LASTPOSITION = eqv;
//that.NID = v.id;
}
})
}
})
};
this.scrollEvent = function () {
var that = this;
$(window).scroll(function () {
var scrollTop = $(window).scrollTop();
var winHeight = $(window).height();
var docHeight = $(document).height();
if (scrollTop + winHeight == docHeight) {
that.fetchImg();
}
})
}
}
6、報障系統
1.對於choice=((),()),obj.status獲取的是數字,obj.get_status_display是文本內容
2018/07/21 day71 權限管理
1.python中 列表、字典 都是引用類型