Django大雜燴

1、web框架

本質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

2、Django

 

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

2018/04/28 週六

概要:路由、視圖、模板、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()

2018/04/29 週日 s20

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])

2018/04/30 週一 s21

知識點
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})

2018/05/01 週二 s22

- 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,若是自定製須要捕捉異常,不然程序報錯

2018/05/03 週四 s24

 

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中 列表、字典 都是引用類型

相關文章
相關標籤/搜索