Django进阶2
一、ORM操作进阶
ForeignKey关联
示例models
from django.db import models # Create your models here. class User(models.Model): name = models.CharField(max_length=32) class Host(models.Model): host_name = models.CharField(max_length=32) blong_to = models.ForeignKey("User")
ForeignKey创建数据
models.Host.objects.create(host_name="127.0.0.1",blong_to=models.User.objects.get(id=1))
1、搜索条件使用 __ 连接 2、获取值时使用 . 连接
user_list=models.Host.objects.filter(blong_to__name="lisi") #一对多过滤条件 for item in user_list: print(item.host_name,item.blong_to.name) #取数据,在前端取数据也类似
ForeignKey修改数据
hosts=models.Host.objects.get(host_name="127.0.0.1") users=models.User.objects.get(id=2) hosts.blong_to=users hosts.save()
反向关联查询
user_obj=models.User.objects.get(id=2) print(user_obj.host_set.select_related())
ManyToManyField关联
示例models
class UserInfo(models.Model): name = models.CharField(max_length=32) email = models.CharField(max_length=32) address = models.CharField(max_length=128) class UserGroup(models.Model): caption = models.CharField(max_length=64) user_info = models.ManyToManyField('UserInfo')
ManyToManyField操作(_set是多对多中的固定搭配)
user_info_obj = models.UserInfo.objects.get(name="zhangsan") 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) # #user_info_obj.usergroup_set.add(group_obj) #user_info_obj.usergroup_set.add(*group_objs) # 删除数据 #group_obj.user_info.remove(user_info_obj) #group_obj.user_info.remove(*user_info_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')
F 对同一表内不同的字段进行对比查询
class Entry(models.Model): n_comments = models.IntegerField() n_pingbacks = models.IntegerField() rating = models.IntegerField()
from django.db.models import F models.Entry.objects.filter(n_comments__gt=F('n_pingbacks')) models.Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2) models.Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))
批量自增
models.Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1)
Q 构建搜索条件
from django.db.models import Q models.UserInfo.objects.get( Q(name='zhangsan'),Q(email="12345678@qq.com")) #两个都要满足 models.UserInfo.objects.get( Q(name='zhangsan') | Q(email="12345678@qq.com")) #只需满足一个
models.UserInfo.objects.get( Q(name='zhangsan'),Q(email="12345678@qq.com") | Q(address="abcde"))
二、django 实现分页
实例
#!/usr/bin/env python # -*- coding:utf-8 -*- from django.shortcuts import render,HttpResponse from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from app01 import models # Create your views here. def stu_login(request): return render(request,"app01/login.html") def stu_home(request): customer_list=models.Customer.objects.all() paginator = Paginator(customer_list, 1) #每页显示条数 page = request.GET.get('page') try: contacts = paginator.page(page) except PageNotAnInteger: # If page is not an integer, deliver first page. contacts = paginator.page(1) except EmptyPage: # If page is out of range (e.g. 9999), deliver last page of results. contacts = paginator.page(paginator.num_pages) return render(request, "app01/home.html", {"customer_list":contacts})
<div class="pagination"> <nav> <ul class="pagination"> {% if customer_list.has_previous %} <li class=""><a href="?page={{ customer_list.previous_page_number }}" aria-label="Previous"><span aria-hidden="true">«</span></a></li> {% endif %} {% for page_num in customer_list.paginator.page_range %} {% if page_num == customer_list.number %}<!--如果page_num是当前选中的页--> <li class="active"><a href="?page={{ page_num }}">{{ page_num }} <span class="sr-only">(current)</span></a></li> {% else %} <li class=""><a href="?page={{ page_num }}">{{ page_num }} <span class="sr-only">(current)</span></a></li> {% endif %} {% endfor %} {% if customer_list.has_next %} <li class=""><a href="?page={{ customer_list.next_page_number }}" aria-label="Next"><span aria-hidden="true">»</span></a></li> {% endif %} </ul> </nav> </div>
优化:固定页码个数
1、自定义template tags
#!/usr/bin/env python # -*- coding:utf-8 -*- from django import template from django.utils.safestring import mark_safe register = template.Library() @register.simple_tag def custemer_paging(current_page,loop_num): #传入选中页和循环页 num_left=current_page-2 num_right=current_page+2 if loop_num>num_left and loop_num<num_right:#只显示3页 if current_page == loop_num: result='''<li class="active"><a href="?page=%s">%s <span class="sr-only">(current)</span></a></li>'''%(loop_num,loop_num) else: result='''<li class=""><a href="?page=%s">%s <span class="sr-only">(current)</span></a></li>'''%(loop_num,loop_num) return mark_safe(result) result="" return mark_safe(result)
在html开头导入
{% load custemer_tags %}
使用自定义simple_tag
<div class="pagination"> <nav> <ul class="pagination"> {% if customer_list.has_previous %} <li class=""><a href="?page={{ customer_list.previous_page_number }}" aria-label="Previous"><span aria-hidden="true">«</span></a></li> {% endif %} {% for page_num in customer_list.paginator.page_range %} {% custemer_paging customer_list.number page_num %}<!--使用custemer_tags--> {% endfor %} {% if customer_list.has_next %} <li class=""><a href="?page={{ customer_list.next_page_number }}" aria-label="Next"><span aria-hidden="true">»</span></a></li> {% endif %} </ul> </nav> </div>
更多详情:https://docs.djangoproject.com/en/1.9/topics/pagination/
三、在自己写的脚本里调用django models
#!/usr/bin/env python # -*- coding:utf-8 -*- import os os.environ['DJANGO_SETTINGS_MODULE'] = 'django_project.settings' import django django.setup() from app01 import models result = models.UserInfo.objects.get(id=1) print(result)
四、用户认证
from django.contrib.auth.models import User class UserProfile(models.Model): user = models.OneToOneField(User) name = models.CharField(max_length=32)
from django.contrib.auth import authenticate,login,logout from django.contrib.auth.decorators import login_required @login_required def acc_home(request): return render(request,"index.html") def acc_login(request): if request.method == "POST": username = request.POST.get("username") password = request.POST.get("password") user = authenticate(username=username,password=password) #验证 if user is not None: login(request,user) #登录 return redirect("/app01/acc_home/") else: return render(request,"login.html") def acc_logout(request): logout(request) #退出
在前端显示用户名或对应的名字
<div> {% if request.user.is_authenticated %} <!--如果已经登录--> <span>{{ request.user }}</span> <!--用户名--> <span>{{ request.user.userprofile.name }}</span> <!--用户名在UserProfile表对应的名字--> {% endif %} </div>
五、权限管理
django 自带有基本的权限管理 ,但粒度和限制权限的维度都只是针对具体的表
自己写的权限要易扩展、灵活,权限系统的设计对开发者、用户要实现透明,即他们不需要改变自己原有的使用系统或调用接口的方式,权限要能实现非常小的粒度的控制,甚至细致到一个按键某个用户是否能按。
想对一个功能实现权限控制,要做到只需在views方法上加一个装饰器就行了
例:
在表内创建一个class Meta,自己定义几个权限。
class UserProfile(models.Model): user = models.OneToOneField(User) name = models.CharField(verbose_name=u"姓名",max_length=32) def __unicode__(self): return self.name class Meta: permissions = (("view_customer_list",u"查看客户信息"), ("view_customer_info",u"查看客户详细信息"), ("view_customer_updata",u"修改详细信息"), )
python manage.py makemigrations
python manage.py migrate
关联动作
这里创建一个permissions.py,创建check_permission装饰器
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 from django.core.urlresolvers import resolve 4 from django.shortcuts import render 5 perm_dic = { 6 "view_customer_list":["customer_list","GET",[]], 7 "view_customer_info":["customer_info","GET",[]], 8 "view_customer_updata":["customer_info","POST",[]], 9 } 10 11 12 def perm_check(*args,**kwargs): 13 request = args[0] 14 url_resovle_obj = resolve(request.path_info) 15 current_url_namespace = url_resovle_obj.url_name 16 #app_name = url_resovle_obj.app_name #use this name later 17 print("url namespace:",current_url_namespace) 18 matched_flag = False # find matched perm item 19 matched_perm_key = None 20 if current_url_namespace is not None:#if didn't set the url namespace, permission doesn't work 21 print("find perm...") 22 for perm_key in perm_dic: 23 perm_val = perm_dic[perm_key] 24 if len(perm_val) == 3:#otherwise invalid perm data format 25 url_namespace,request_method,request_args = perm_val 26 print(url_namespace,current_url_namespace) 27 if url_namespace == current_url_namespace: #matched the url 28 if request.method == request_method:#matched request method 29 if not request_args:#if empty , pass 30 matched_flag = True 31 matched_perm_key = perm_key 32 print('mtched...') 33 break #no need looking for other perms 34 else: 35 for request_arg in request_args: #might has many args 36 request_method_func = getattr(request,request_method) #get or post mostly 37 #print("----->>>",request_method_func.get(request_arg)) 38 if request_method_func.get(request_arg) is not None: 39 matched_flag = True # the arg in set in perm item must be provided in request data 40 else: 41 matched_flag = False 42 print("request arg [%s] not matched" % request_arg) 43 break #no need go further 44 if matched_flag == True: # means passed permission check ,no need check others 45 print("--passed permission check--") 46 matched_perm_key = perm_key 47 break 48 49 else:#permission doesn't work 50 return True 51 52 if matched_flag == True: 53 #pass permission check 54 perm_str = "app01.%s" %(matched_perm_key) 55 if request.user.has_perm(perm_str): 56 print("\033[42;1m--------passed permission check----\033[0m") 57 return True 58 else: 59 print("\033[41;1m ----- no permission ----\033[0m") 60 print(request.user,perm_str) 61 return False 62 else: 63 print("\033[41;1m ----- no matched permission ----\033[0m") 64 65 def check_permission(func): 66 def wrapper(*args,**kwargs): 67 print("---start check perms",args[0]) 68 if not perm_check(*args,**kwargs): 69 return render(args[0],'app01/403.html') 70 return func(*args,**kwargs) 71 #print("---done check perms") 72 return wrapper
url(r'^stu_home/$', views.stu_home,name="customer_list"), url(r'^stu_detail/(\d+)/$', views.stu_detail,name="customer_info"),
在views导入自己写的装饰器并在相应方法上加一个装饰器
可以用超级用户通过admin对普通用户进行权限控制
六、CSRF(跨站域请求伪造)
CSRF(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安全隐患之一
防御策略
在请求地址中添加 token 并验证
Django 中使用CSRF,在表单下使用{% csrf_token %}
<form action="" method="post">{% csrf_token %}
AJAX中CSRF
function getCookie(name) { var cookieValue = null; if (document.cookie && document.cookie != '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = jQuery.trim(cookies[i]); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) == (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } var csrftoken = getCookie('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); } } });