Django基础(二)
上节回顾
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(或黑名单)
- 用户管理、用户组管理、主机管理(增删改查,分页)
- 自定义页面表格行数(可选)