django进阶篇

前端模板

models.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#_*_coding:utf-8_*_
from django.db import models
 
# Create your models here.
from django.core.exceptions import ValidationError
 
from django.db import models
from django.contrib.auth.models import User
course_type_choices = (('online',u'网络班'),
                        ('offline_weekend',u'面授班(周末)',),
                        ('offline_fulltime',u'面授班(脱产)',),
                        )
class School(models.Model):
    name = models.CharField(max_length=128,unique=True)
    city = models.CharField(max_length=64)
    addr = models.CharField(max_length=128)
 
    def __str__(self):
        return self.name
 
class UserProfile(models.Model):
    user = models.OneToOneField(User) #alex
    name = models.CharField(max_length=64)
    school = models.ForeignKey('School')
 
    def __str__(self):
        return self.name
 
class Customer(models.Model):
    qq = models.CharField(max_length=64,unique=True)
    name = models.CharField(max_length=32,blank=True,null=True)
    phone = models.BigIntegerField(blank=True,null=True)
    course = models.ForeignKey('Course')
 
    course_type = models.CharField(max_length=64,choices=course_type_choices,default='offline_weekend')
    consult_memo = models.TextField()
    source_type_choices = (('qq',u"qq群"),
                   ('referral',u"内部转介绍"),
                   ('51cto',u"51cto"),
                   ('agent',u"招生代理"),
                   ('others',u"其它"),
                   )
    source_type = models.CharField(max_length=64,choices=source_type_choices)
    referral_from = models.ForeignKey('self',blank=True,null=True,related_name="referraled_who")
    status_choices = (('signed',u"已报名"),
                      ('unregistered',u"未报名"),
                      ('graduated',u"已毕业"),
                      ('drop-off',u"退学"),
                      )
    status = models.CharField(choices=status_choices,max_length=64)
    consultant = models.ForeignKey('UserProfile',verbose_name=u"课程顾问")
    class_list = models.ManyToManyField("ClassList",blank=True)
    date = models.DateField(u"咨询日期",auto_now_add=True)
 
    def __str__(self):
        return "%s(%s)"%(self.qq,self.name)
class CustomerTrackRecord(models.Model):
    customer = models.ForeignKey(Customer)
    track_record = models.TextField(u"跟踪纪录")
    track_date = models.DateField(auto_now_add=True)
    follower = models.ForeignKey(UserProfile)
    status_choices = ((1,u"近期无报名计划"),
                      (2,u"2个月内报名"),
                      (3,u"1个月内报名"),
                      (4,u"2周内报名"),
                      (5,u"1周内报名"),
                      (6,u"2天内报名"),
                      (7,u"已报名"),
                      )
    status = models.IntegerField(u"状态",choices=status_choices,help_text=u"选择客户此时的状态")
    def __str__(self):
        return self.customer
 
class Course(models.Model):
    name = models.CharField(max_length=64,unique=True)
    online_price = models.IntegerField()
    offline_price = models.IntegerField()
    introduction = models.TextField()
 
    def __str__(self):
        return self.name
class ClassList(models.Model):
    course = models.ForeignKey(Course,verbose_name=u"课程")
    semester = models.IntegerField(verbose_name=u"学期")
    course_type = models.CharField(max_length=64,choices=course_type_choices,default='offline_weekend')
    teachers = models.ManyToManyField(UserProfile)
    start_date = models.DateField()
    graduate_date = models.DateField()
 
    def __str__(self):
        return "%s(%s)(%s)" %(self.course.name,self.course_type,self.semester)
 
    class Meta:
        unique_together = ('course','semester','course_type')
 
class CourseRecord(models.Model):
    class_obj = models.ForeignKey(ClassList)
    day_num = models.IntegerField(u"第几节课")
    course_date = models.DateField(auto_now_add=True,verbose_name=u"上课时间")
    teacher = models.ForeignKey(UserProfile)
    #students = models.ManyToManyField(Customer)
    def __str__(self):
        return "%s,%s"%(self.class_obj,self.day_num)
    class Meta:
        unique_together = ('class_obj','day_num')
 
class StudyRecord(models.Model):
    course_record = models.ForeignKey(CourseRecord)
    student = models.ForeignKey(Customer)
    record_choices = (('checked', u"已签到"),
                      ('late',u"迟到"),
                      ('noshow',u"缺勤"),
                      ('leave_early',u"早退"),
                      )
    record = models.CharField(u"状态", choices=record_choices,max_length=64)
    score_choices = ((100, 'A+'),
                     (90,'A'),
                     (85,'B+'),
                     (80,'B'),
                     (70,'B-'),
                     (60,'C+'),
                     (50,'C'),
                     (40,'C-'),
                     (0,'D'),
                     (-1,'N/A'),
                     (-100,'COPY'),
                     (-1000,'FAIL'),
                     )
    score = models.IntegerField(u"本节成绩",choices=score_choices,default=-1)
    date = models.DateTimeField(auto_now_add=True)
    note = models.CharField(u"备注",max_length=255,blank=True,null=True)
 
    def __str__(self):
        return "%s,%s,%s"%(self.course_record,self.student,self.record)


 一、静态文件目录设置

在settings.py文件中加入:

1
2
3
4
STATIC_URL = '/static/'
STATICFILES_DIRS = (
    os.path.join(BASE_DIR,'static'),
)

STATIC_URL为静态文件目录的别名,引用静态文件方法:


STATICFILES_DIRS为静态文件实际目录,其中可以有多个路径

二、bootstrap模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<table class="table table-hover">
        <thead>
            <tr>
                <th>ID</th>
                <th>QQ</th>
                <th>姓名</th>
                <th>渠道</th>
                <th>咨询课程</th>
                <th>课程类型</th>
                <th>客户备注</th>
                <th>状态</th>
                <th>课程顾问</th>
                <th>日期</th>
            </tr>
        </thead>
        <tbody>
            {% for customer in custmer_list %}
                <tr>
                    <td>{{ customer.id }}</td>
                    <td>{{ customer.qq }}</td>
                    <td>{{ customer.name }}</td>
                    <td>{{ customer.get_source_type_display }}</td>}
                    <td>{{ customer.course }}</td>
                    <td>{{ customer.get_course_type_display }}</td>
                    <td>{{ customer.consult_memo | truncatechars:20 }}</td>
                    <td class="{{ customer.status }}">{{ customer.get_status_display }}</td>
                    <td>{{ customer.consultant }}</td>
                    <td>{{ customer.date }}</td>
                </tr>
            {% endfor %}
        </tbody>
    </table>

get_source_type_display作用是:



 truncatechars:20作用是前端只显示20个字符

分页

django文档中的使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
from django.core.paginator import Paginator
>>> objects = ['john', 'paul', 'george', 'ringo']
>>> p = Paginator(objects, 2)
 
>>> p.count
4
>>> p.num_pages
2
>>> type(p.page_range)  # `<type 'rangeiterator'>` in Python 2.
<class 'range_iterator'>
>>> p.page_range
range(1, 3)
 
>>> page1 = p.page(1)
>>> page1
<Page 1 of 2>
>>> page1.object_list
['john', 'paul']
 
>>> page2 = p.page(2)
>>> page2.object_list
['george', 'ringo']
>>> page2.has_next()
False
>>> page2.has_previous()
True
>>> page2.has_other_pages()
True
>>> page2.next_page_number()
Traceback (most recent call last):
...
EmptyPage: That page contains no results
>>> page2.previous_page_number()
1
>>> page2.start_index() # The 1-based index of the first item on this page
3
>>> page2.end_index() # The 1-based index of the last item on this page
4
 
>>> p.page(0)
Traceback (most recent call last):
...
EmptyPage: That page number is less than 1
>>> p.page(3)
Traceback (most recent call last):
...
EmptyPage: That page contains no results

在视图中的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from django.shortcuts import render
from app01 import models
from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger
def custmer(request):
 
    custmer_list = models.Customer.objects.all()
    paginator = Paginator(custmer_list,1)
    #前端提交的需要取哪一行
    page = request.GET.get('page')
    try:
        custmer_objs = paginator.page(page)
    #如果输入的不是数字,就返回第一页
    except PageNotAnInteger:
        custmer_objs = paginator.page(1)
    #若果超出了就显示最后一页
    except EmptyPage:
        custmer_objs = paginator.page(paginator.num_pages)
    return render(request,'crm/custme.html',{'custmer_list':custmer_objs})

分页样式要是好看一点需要自定义模板标签:

polls/                 你的app名字
    __init__.py
    models.py
    templatetags/       自定义标签目录,名字必须为这个
        __init__.py
        poll_extras.py  你自己的标签文件,可自定义

views.py


我的标签文件名字是page.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env python
 
from django import template
from django.utils.html import format_html
register = template.Library()
#当函数参数大于一个用这个装饰器
@register.simple_tag
def guess_page(current_page,loop_num):
    '''
        current_page:前端返回的当前页数
        loop_num:前端要展示第几页到第几页
    '''
    #当前页左右的页数
    offset = abs(current_page-loop_num)
    #当前页前后显示的页数小于3
    if offset < 3:
        if current_page == loop_num:
            page_ele = '''<li class="active"><a href="?page=%s">%s</a></li>''' %(loop_num,loop_num)
        else:
            page_ele = '''<li class=""><a href="?page=%s">%s</a></li>''' %(loop_num,loop_num)
        return format_html(page_ele)
    else:
        return ''

前端模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% load page %}  #导入自定义模板标签
                {% if  custmer_list.has_previous%}
                    <li class=""><a href="?page={{ custmer_list.previous_page_number }}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>
                {% else %}
                    <li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>
                {% endif %}
                {% for page_num in custmer_list.paginator.page_range %}
                    {% guess_page custmer_list.number page_num %}
                {% endfor %}
                {% if  custmer_list.has_next%}
                    <li class=""><a href="?page={{ custmer_list.next_page_number }}" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>
                {% else %}
                    <li class="disabled"><a href="#" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>
                {% endif %}


{% guess_page custmer_list.number page_num %}其中将custmer_list.number当做第一个参数,page_num当做第二个参数传给guess_page我们写好的自定义标签,计算结果返回在前端显示

Bootstrap分页模板标签:

如果你想将整个分页都放到后台模板标签中也是可以的,具体做法如下:

在page.py文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/env python
 
from django import template
from django.utils.html import format_html
register = template.Library()
 
#当函数只有一个参数时用register.filter装饰器
@register.filter
def page_num(page):
    '''
        page为后端传到前端的paginator.page的对象
    '''
    pre_page = ''
    if page.has_previous():
        pre_page = '''<li class=""><a href="?page=%s" aria-label="Previous">
        <span aria-hidden="true">&laquo;</span></a></li>''' %page.previous_page_number()
    page_ele = ''
    for loop_num in page.paginator.page_range:
        offset = abs(page.number-loop_num)
        #当前页显示前后位3页,需要时可以微调
        if offset < 3:
            if page.number == loop_num:
                page_ele += '''<li class="active"><a href="?page=%s">%s</a></li>''' %(loop_num,loop_num)
            else:
                page_ele += '''<li class=""><a href="?page=%s">%s</a></li>''' %(loop_num,loop_num)
    next_page = ''
    if page.has_next():
        next_page = '''<li class=""><a href="?page=%s" aria-label="Next">
        <span aria-hidden="true">&raquo;</span></a></li>''' %page.next_page_number()
    return format_html(pre_page+page_ele+next_page)

前端模板文件内容:

1
2
3
4
5
6
<nav>
       <ul class="pagination">
         {{ custmer_list | page_num }}
 
       </ul>
</nav>

custmer_list为后端传过来的paginator.page对象,显示效果为:


ModelForm进阶

需求为显示每一个学员的详细信息

先在项目中添加一个forms文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env python
 
from django.forms import Form,ModelForm
from app01 import models
 
class CustomerModelForm(ModelForm):
 
    class Meta:
 
        model = models.Customer
 
        exclude = ()
    def __init__(self,*args,**kwargs):
        #重写init方法
        super(CustomerModelForm,self).__init__(*args,**kwargs)
        #给其中一个字段添加样式
        #self.fields['qq'].widget.attrs['class'] = 'form-contral'
        #给所有字段统一添加样式
        for field_name in self.base_fields:
            field = self.base_fields[field_name]
            field.widget.attrs.update({'class':'form-control'})

视图:

1
2
3
4
5
6
7
8
9
10
from app01 import forms
def customer_detail(request,customer_id):
 
    '''
       customer_id为前端url中的最后一位
    '''
    customer_obj = models.Customer.objects.get(id=customer_id)
    #将查找到的客户信息放入前端表单中
    form = forms.CustomerModelForm(instance=customer_obj)
    return render(request,'crm/customer_detail.html',{'customer_form':form})

效果:


 前端页面好看一些,还有加上表单功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<form method="post" action="" class="form-horizontal">{% csrf_token %}
        {% for field in customer_form %}
            <div class="form-group">
                <label class="col-sm-2 control-label">{{ field.label }}</label>
                <div class="col-sm-8">
                    {{ field }}
                </div>
            </div>
        {% endfor %}
        <div class="form-group">
            <div class="col-sm-2 col-sm-offset-8">
                 <input class="btn btn-success pull-right" type="submit" value="Save"/>
            </div>
        </div>
    </form>

前端显示为:


加上保存功能,修改视图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from django.shortcuts import render
from app01 import models
from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger
# Create your views here.
from app01 import forms
def customer_detail(request,customer_id):
 
    '''
       customer_id为前端url中的最后一位
    '''
    customer_obj = models.Customer.objects.get(id=customer_id)
    if request.method == 'POST':
        form = forms.CustomerModelForm(request.POST,instance=customer_obj)
        #验证
        if form.is_valid():
            #保存
            form.save()
    #将查找到的客户信息放入前端表单中
    else:
        form = forms.CustomerModelForm(instance=customer_obj)
    return render(request,'crm/customer_detail.html',{'customer_form':form})

保存之后返回客户页面:


其中request.path为前端请求的url

输出错误信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<form method="post" action="" class="form-horizontal">{% csrf_token %}
        {% for field in customer_form %}
            <div class="form-group">
                <label class="col-sm-2 control-label">{{ field.label }}</label>
                <div class="col-sm-8">
                    {{ field }}
                    {% if field.errors %}
                        <ul>
                            {% for error in field.errors %}
                                <li style="color: red">
                                    {{ error }}
                                </li>
                            {% endfor %}
                        </ul>
                    {% endif %}
                </div>
            </div>
        {% endfor %}
        <div class="form-group">
            <div class="col-sm-2 col-sm-offset-8">
                 <input class="btn btn-success pull-right" type="submit" value="Save"/>
            </div>
        </div>
    </form>

判断字段是否为必填:


权限管理

django 自带有基本的权限管理 ,但粒度和限制权限的维度都只是针对具体的表,如果我们想根据业务功能来限制权限,那就得自己写了, 不过也不用完全自己的写,我们可以在django 自带的权限基础上轻松的实现扩展。 

自己写权限要注意:

  1. 权限系统的设计对开发者、用户要实现透明,即他们不需要改变自己原有的使用系统或调用接口的方式

  2. 权限要易扩展,灵活

  3. 权限要能实现非常小的粒度的控制,甚至细致到一个按键某个用户是否能按。

添加权限,在models中的任意表都可以添加,如:


不要忘记更新表结构

在admin中就可以看到这一条权限:


可以调用user.has_perm('项目名.数据库中的字段名')来判断这个用户是否有这个权限,其中用户为django的用户

示例如下:


Django url别名:

前端应用:


customer.id是参数,需要传进url中的

前端url别名获取:


其中url可以用request.path_info来获取

自己写一个权限控制:

想对一个功能实现权限控制,要做到只能过在views方法上加一个装饰器就行了,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@check_permission
@login_required
def customer_detail(request,customer_id):
    customer_obj = models.Customer.objects.get(id=customer_id)
    customer_form = forms.CustomerDetailForm(instance=customer_obj)
  
    if request.method == 'POST':
        customer_form = forms.CustomerDetailForm(request.POST,instance=customer_obj)
        if customer_form.is_valid():
            customer_form.save()
            parent_base_url = '/'.join(request.path.split('/')[:-2])
            print("url:",parent_base_url )
            return  redirect(parent_base_url)
        else:
            print(customer_form.errors)
    return  render(request,'crm/customer_detail.html',{'customer_form':customer_form})

在models里面修改表结构:

1
2
3
4
5
6
7
8
9
10
11
12
class UserProfile(models.Model):
    user = models.OneToOneField(User) #alex
    name = models.CharField(max_length=64)
    school = models.ForeignKey('School')
 
    def __unicode__(self):
        return self.name
    class Meta:
        permissions =(('view_customer_list', u"可以查看客户列表"),
                      ('view_customer_info',u"可以查看客户详情"),
                      ('edit_own_customer_info',u"可以修改自己的客户信息"),
                      )

写一个权限文件,名字可以随意:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author:Alex Li
from django.core.urlresolvers import resolve
from django.shortcuts import render
#权限控制字典,url别名,提交方法,属性综合判断那种权限
perm_dic = {
    'view_customer_list': ['customer_list','GET',[]], 
    'view_customer_info': ['customer_detail','GET',[]],
    'edit_own_customer_info': ['customer_detail','POST',['qq','name']],
}
#权限控制主要逻辑函数
def perm_check(*args,**kwargs):
    #views函数中的request
    request = args[0]
    url_resovle_obj = resolve(request.path_info)
    #url别名
    current_url_namespace = url_resovle_obj.url_name
    #匹配标识符
    matched_flag = False
    #匹配的key值
    matched_perm_key = None
    #url别名存在
    if current_url_namespace is not None:
        print("find perm...")
        #遍历权限字典
        for perm_key in perm_dic:
            #权限字典的值
            perm_val = perm_dic[perm_key]
            #权限的值个数必须为3个
            if len(perm_val) == 3:
                #url别名,提交方法,属性列表分别赋值
                url_namespace,request_method,request_args = perm_val
                #url别名匹配
                if url_namespace == current_url_namespace:
                    #提交方式匹配
                    if request.method == request_method:
                        #属性列表为空就匹配上了,跳出循环
                        if not request_args:#if empty , pass
                            matched_flag = True
                            matched_perm_key = perm_key
                            print('mtched...')
                            break
                        #属性列表不为空
                        else:
                            #循环属性列表
                            for request_arg in request_args:
                                #利用反射知道是post还get方式并得到request.POST或者request.GET
                                request_method_func = getattr(request,request_method)
                                #如果有这个属性就代表这条属性匹配上
                                if request_method_func.get(request_arg) is not None:
                                    matched_flag = True
                                else:
                                #如果没有这条属性直接跳出
                                    matched_flag = False
                                    print("request arg [%s] not matched" % request_arg)
                                    break
                            #匹配上赋值
                            if matched_flag == True:
                                print("--passed permission check--")
                                matched_perm_key = perm_key
                                break
 
    else:
        return True
    #如果匹配上了
    if matched_flag == True:
        #将字符串拼接
        perm_str = "crm.%s" %(matched_perm_key) #crm.view_customer_list
        #查询这个用户有这条权限
        if request.user.has_perm(perm_str):
            print("\033[42;1m--------passed permission check----\033[0m")
            return True
        else:
            #如果没有权限返回False
            print("\033[41;1m ----- no permission ----\033[0m")
            print(request.user,perm_str)
            return False
    else:
        print("\033[41;1m ----- no matched permission  ----\033[0m")
 
#写一个验证权限装饰器
def check_permission(func):
    def wrapper(*args,**kwargs):
        print('---start check perm---')
        #如果没匹配上就跳转到403页面
        if perm_check(*args,**kwargs) is not True:#no permisssion
            return render(args[0],'crm/403.html')
        return func(*args,**kwargs)
    return  wrapper




来自为知笔记(Wiz)


posted on 2016-06-02 14:05  显卡  阅读(416)  评论(0编辑  收藏  举报

导航