上節回顧
web程序的生命周期
MVC和MTV
路由系統和自定義動態路由
Model
模版語言 和 simple_tag
7、Model連表操作
- 指定映射
- 連表條件
- 一對一
- 多對多
class UserProfile(models.Model): user_info = models.OneToOneField('UserInfo') username = models.CharField(max_length=64) password = models.CharField(max_length=64) def __unicode__(self): return self.username class UserInfo(models.Model): user_type_choice = ( (0, u'普通用戶'), (1, u'高級用戶'), ) user_type = models.IntegerField(choices=user_type_choice) name = models.CharField(max_length=32) email = models.CharField(max_length=32) address = models.CharField(max_length=128) def __unicode__(self): return self.name class UserGroup(models.Model): caption = models.CharField(max_length=64) user_info = models.ManyToManyField('UserInfo') def __unicode__(self): return self.caption class Host(models.Model): hostname = models.CharField(max_length=64) ip = models.GenericIPAddressField() user_group = models.ForeignKey('UserGroup') def __unicode__(self): return self.hostname
一對一
user_info_obj = models.UserInfo.objects.filter(id=1).first()
print user_info_obj.user_type
print user_info_obj.get_user_type_display()
print user_info_obj.userprofile.password
user_info_obj = models.UserInfo.objects.filter(id=1).values('email', 'userprofile__username').first()
print user_info_obj.keys()
print user_info_obj.values()
多對多表之間的查詢
user_info_obj = models.UserInfo.objects.get(name=u'武沛齊')
user_info_objs = models.UserInfo.objects.all()
group_obj = models.UserGroup.objects.get(caption='CEO')
group_objs = models.UserGroup.objects.all()
# 添加數據
#group_obj.user_info.add(user_info_obj)
#group_obj.user_info.add(*user_info_objs)
# 刪除數據
#group_obj.user_info.remove(user_info_obj)
#group_obj.user_info.remove(*user_info_objs)
# 添加數據
#user_info_obj.usergroup_set.add(group_obj)
#user_info_obj.usergroup_set.add(*group_objs)
# 刪除數據
#user_info_obj.usergroup_set.remove(group_obj)
#user_info_obj.usergroup_set.remove(*group_objs)
# 獲取數據
#print group_obj.user_info.all()
#print group_obj.user_info.all().filter(id=1)
# 獲取數據
#print user_info_obj.usergroup_set.all()
#print user_info_obj.usergroup_set.all().filter(caption='CEO')
#print user_info_obj.usergroup_set.all().filter(caption='DBA')
注意:xx_set中的【_set】是固定搭配
8、Form
django中的Form一般有兩種功能:
- 輸入html
- 驗證用戶輸入
#!/usr/bin/env python # -*- coding:utf-8 -*- import re from django import forms from django.core.exceptions import ValidationError def mobile_validate(value): mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') if not mobile_re.match(value): raise ValidationError('手機號碼格式錯誤') class PublishForm(forms.Form): user_type_choice = ( (0, u'普通用戶'), (1, u'高級用戶'), ) user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice, attrs={'class': "form-control"})) title = forms.CharField(max_length=20, min_length=5, error_messages={'required': u'標題不能為空', 'min_length': u'標題最少為5個字符', 'max_length': u'標題最多為20個字符'}, widget=forms.TextInput(attrs={'class': "form-control", 'placeholder': u'標題5-20個字符'})) memo = forms.CharField(required=False, max_length=256, widget=forms.widgets.Textarea(attrs={'class': "form-control no-radius", 'placeholder': u'詳細描述', 'rows': 3})) phone = forms.CharField(validators=[mobile_validate, ], error_messages={'required': u'手機不能為空'}, widget=forms.TextInput(attrs={'class': "form-control", 'placeholder': u'手機號碼'})) email = forms.EmailField(required=False, error_messages={'required': u'郵箱不能為空','invalid': u'郵箱格式錯誤'}, widget=forms.TextInput(attrs={'class': "form-control", 'placeholder': u'郵箱'})) Form
def publish(request): ret = {'status': False, 'data': '', 'error': '', 'summary': ''} if request.method == 'POST': request_form = PublishForm(request.POST) if request_form.is_valid(): request_dict = request_form.clean() print request_dict ret['status'] = True else: error_msg = request_form.errors.as_json() ret['error'] = json.loads(error_msg) return HttpResponse(json.dumps(ret))
擴展:ModelForm
在使用Model和Form時,都需要對字段進行定義並指定類型,通過ModelForm則可以省去From中字段的定義
class AdminModelForm(forms.ModelForm):
class Meta:
model = models.Admin
#fields = '__all__'
fields = ('username', 'email')
widgets = {
'email' : forms.PasswordInput(attrs={'class':"alex"}),
}
9、分頁
分頁功能在每個網站都是必要的,對於分頁來說,其實就是根據用戶的輸入計算出應該在數據庫表中的起始位置。
1、設定每頁顯示數據條數
2、用戶輸入頁碼(第一頁、第二頁...)
3、根據設定的每頁顯示條數和當前頁碼,計算出需要取數據表的起始位置
4、在數據表中根據起始位置取值,頁面上輸出數據
需求又來了,需要在頁面上顯示分頁的頁面。如:[上一頁][1][2][3][4][5][下一頁]
1、設定每頁顯示數據條數
2、用戶輸入頁碼(第一頁、第二頁...)
3、設定顯示多少頁號
4、獲取當前數據總條數
5、根據設定顯示多少頁號和數據總條數計算出,總頁數
6、根據設定的每頁顯示條數和當前頁碼,計算出需要取數據表的起始位置
7、在數據表中根據起始位置取值,頁面上輸出數據
8、輸出分頁html,如:[上一頁][1][2][3][4][5][下一頁]
#!/usr/bin/env python
# _*_coding:utf-8_*_
from django.utils.safestring import mark_safe
class PageInfo(object):
def __init__(self,current,totalItem,peritems=5):
self.__current=current
self.__peritems=peritems
self.__totalItem=totalItem
def From(self):
return (self.__current-1)*self.__peritems
def To(self):
return self.__current*self.__peritems
def TotalPage(self): #總頁數
result=divmod(self.__totalItem,self.__peritems)
if result[1]==0:
return result[0]
else:
return result[0]+1
def Custompager(baseurl,currentPage,totalpage): #基礎頁,當前頁,總頁數
perPager=11
#總頁數<11
#0 -- totalpage
#總頁數>11
#當前頁大於5 currentPage-5 -- currentPage+5
#currentPage+5是否超過總頁數,超過總頁數,end就是總頁數
#當前頁小於5 0 -- 11
begin=0
end=0
if totalpage <= 11:
begin=0
end=totalpage
else:
if currentPage>5:
begin=currentPage-5
end=currentPage+5
if end > totalpage:
end=totalpage
else:
begin=0
end=11
pager_list=[]
if currentPage<=1:
first="<a href=''>首頁</a>"
else:
first="<a href='%s%d'>首頁</a>" % (baseurl,1)
pager_list.append(first)
if currentPage<=1:
prev="<a href=''>上一頁</a>"
else:
prev="<a href='%s%d'>上一頁</a>" % (baseurl,currentPage-1)
pager_list.append(prev)
for i in range(begin+1,end+1):
if i == currentPage:
temp="<a href='%s%d' class='selected'>%d</a>" % (baseurl,i,i)
else:
temp="<a href='%s%d'>%d</a>" % (baseurl,i,i)
pager_list.append(temp)
if currentPage>=totalpage:
next="<a href='#'>下一頁</a>"
else:
next="<a href='%s%d'>下一頁</a>" % (baseurl,currentPage+1)
pager_list.append(next)
if currentPage>=totalpage:
last="<a href=''>末頁</a>"
else:
last="<a href='%s%d'>末頁</a>" % (baseurl,totalpage)
pager_list.append(last)
result=''.join(pager_list)
return mark_safe(result) #把字符串轉成html語言
總結,分頁時需要做三件事:
- 創建處理分頁數據的類
- 根據分頁數據獲取數據
- 輸出分頁HTML,即:[上一頁][1][2][3][4][5][下一頁]
10、Cookie和Session
操作Cookie
獲取cookie:request.COOKIES[key]
設置cookie:response.set_cookie(key,value)
由於cookie保存在客戶端的電腦上,所以,jquery也可以操作cookie。
<script src='/static/js/jquery.cookie.js'></script>
$.cookie("list_pager_num", 30,{ path: '/' });
操作Session
獲取session:request.session[key]
設置session:reqeust.session[key] = value
刪除session:del request[key]
request.session.set_expiry(value) * 如果value是個整數,session會在些秒數后失效。 * 如果value是個datatime或timedelta,session就會在這個時間后失效。 * 如果value是0,用戶關閉瀏覽器session就會失效。 * 如果value是None,session會依賴全局session失效策略。
更多見:http://docs.30c.org/djangobook2/chapter14/
應用
def login(func):
def wrap(request, *args, **kwargs):
# 如果未登陸,跳轉到指定頁面
if request.path == '/test/':
return redirect('http://www.baidu.com')
return func(request, *args, **kwargs)
return wrap
11、Ajax
$.ajax({
url:"/web/index/",
data:{id:1,name:'alex'},
type:'POST',
success:function(data){
},
error:function(){
}
});
12、跨站請求偽造
簡介
django為用戶實現防止跨站請求偽造的功能,通過中間件 django.middleware.csrf.CsrfViewMiddleware 來完成。而對於django中設置防跨站請求偽造功能有分為全局和局部。
全局:
中間件 django.middleware.csrf.CsrfViewMiddleware
局部:
- @csrf_protect,為當前函數強制設置防跨站請求偽造功能,即便settings中沒有設置全局中間件。
- @csrf_exempt,取消當前函數防跨站請求偽造功能,即便settings中設置了全局中間件。
注:from django.views.decorators.csrf import csrf_exempt,csrf_protect
應用
1、普通表單
veiw中設置返回值:
return render_to_response('Account/Login.html',data,context_instance=RequestContext(request))
html中設置Token:
{% csrf_token %}
2、Ajax
對於傳統的form,可以通過表單的方式將token再次發送到服務端,而對於ajax的話,使用如下方式。
view.py
from django.template.context import RequestContext
# Create your views here.
def test(request):
if request.method == 'POST':
print request.POST
return HttpResponse('ok')
return render_to_response('app01/test.html',context_instance=RequestContext(request))
text.html
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
{% csrf_token %}
<input type="button" onclick="Do();" value="Do it"/>
<script src="/static/plugin/jquery/jquery-1.8.0.js"></script>
<script src="/static/plugin/jquery/jquery.cookie.js"></script>
<script type="text/javascript">
var csrftoken = $.cookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
function Do(){
$.ajax({
url:"/app01/test/",
data:{id:1},
type:'POST',
success:function(data){
console.log(data);
}
});
}
</script>
</body>
</html>
更多:https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
13、中間件
簡介
django 中的中間件(middleware),在django中,中間件其實就是一個類,在請求到來和結束后,django會根據自己的規則在合適的時機執行中間件中相應的方法。
在django項目的settings模塊中,有一個 MIDDLEWARE_CLASSES 變量,其中每一個元素就是一個中間件,如下圖。

與mange.py在同一目錄下的文件夾 wupeiqi/middleware下的auth.py文件中的Authentication類
中間件中可以定義四個方法,分別是:
- process_request(self,request)
- process_view(self, request, callback, callback_args, callback_kwargs)
- process_exception(self, request, exception)
- process_response(self, request, response)
以上方法的返回值可以是None和HttpResonse對象,如果是None,則繼續按照django定義的規則向下執行,如果是HttpResonse對象,則直接將該對象返回給用戶。

自定義中間件
1、創建中間件類
class RequestExeute(object):
def process_request(self,request):
pass
def process_view(self, request, callback, callback_args, callback_kwargs):
i =1
pass
def process_exception(self, request, exception):
pass
def process_response(self, request, response):
return response
2、注冊中間件
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'wupeiqi.middleware.auth.RequestExeute',
)
14、admin
django amdin是django提供的一個后台管理頁面,改管理頁面提供完善的html和css,使得你在通過Model創建完數據庫表之后,就可以對數據進行增刪改查,而使用django admin 則需要以下步驟:
- 創建后台管理員
- 配置url
- 注冊和配置django admin后台管理頁面
1、創建后台管理員
python manage.py createsuperuser
2、配置后台管理url
url(r'^admin/', include(admin.site.urls))
3、注冊和配置django admin 后台管理頁面
a、在admin中執行如下配置
from django.contrib import admin from app01 import models admin.site.register(models.UserType) admin.site.register(models.UserInfo) admin.site.register(models.UserGroup) admin.site.register(models.Asset)
b、設置數據表名稱
class UserType(models.Model):
name = models.CharField(max_length=50)
class Meta:
verbose_name = '用戶類型'
verbose_name_plural = '用戶類型'
c、打開表之后,設定默認顯示,需要在model中作如下配置
class UserType(models.Model):
name = models.CharField(max_length=50)
def __unicode__(self):
return self.name
from django.contrib import admin
from app01 import models
class UserInfoAdmin(admin.ModelAdmin):
list_display = ('username', 'password', 'email')
admin.site.register(models.UserType)
admin.site.register(models.UserInfo,UserInfoAdmin)
admin.site.register(models.UserGroup)
admin.site.register(models.Asset)
d、為數據表添加搜索功能
from django.contrib import admin
from app01 import models
class UserInfoAdmin(admin.ModelAdmin):
list_display = ('username', 'password', 'email')
search_fields = ('username', 'email')
admin.site.register(models.UserType)
admin.site.register(models.UserInfo,UserInfoAdmin)
admin.site.register(models.UserGroup)
admin.site.register(models.Asset)
e、添加快速過濾
from django.contrib import admin
from app01 import models
class UserInfoAdmin(admin.ModelAdmin):
list_display = ('username', 'password', 'email')
search_fields = ('username', 'email')
list_filter = ('username', 'email')
admin.site.register(models.UserType)
admin.site.register(models.UserInfo,UserInfoAdmin)
admin.site.register(models.UserGroup)
admin.site.register(models.Asset)
更多:http://docs.30c.org/djangobook2/chapter06/
完善主機管理系統:
- 登陸注冊(利用ajax實現,使用onblur事件,檢測當前用戶名是否已經存在)
- 用戶會話保持(Session)
- Form驗證用戶輸入
- 自定義中間件記錄請求IP(或黑名單)
- 用戶管理、用戶組管理、主機管理(增刪改查,分頁)
- 自定義頁面表格行數(可選)
