前言: 今晚写一篇关于学员/讲师/销售员CRM系统。这个小项目是27号开始做的,大概搞了一星期不到。我把一些知识点总结下,还写下当时克服的BUG。

 

Django练习小项目:学员管理系统设计开发

带着项目需求学习是最有趣和效率最高的,今天就来基于下面的需求来继续学习Django 

项目需求:

  1. 分讲师\学员\课程顾问角色

  2. 学员可以属于多个班级,学员成绩按课程分别统计

  3. 每个班级至少包含一个或多个讲师

  4. 一个学员要有状态转化的过程 ,比如未报名前,报名后,毕业老学员

  5. 客户要有咨询纪录, 后续的定期跟踪纪录也要保存

  6. 每个学员的所有上课出勤情况\学习成绩都要保存

  7. 学校可以有分校区,默认每个校区的员工只能查看和管理自己校区的学员

  8. 客户咨询要区分来源

拿到需求后,先要分析,再设计表结构: 超级重要!!

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训 View Code

 

先来张图看看效果: 下图是销售员Alex登陆后看到的界面

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

点击右上方Alex已招学员,出现下图界面:

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

 

一、前端界面实现

界面看着我感觉是蛮漂亮的,登陆界面信息界面都是搞bootstrap模版的。只要将bootstrap模版修改下,就变成所需要的界面啦。不会修改的可以看看如何使用bootstrap

 

二、字数显示限制

如果备注过多,会使界面不好看,要想使备注只显示一定的字数,可用下列方法: 只显示13个字节

1
<td>{{ customer.consult_memo|truncatechars:13}}</td>

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

 

三、报名状态加色

第一种方法,比较麻烦,有兴趣可看django进阶-modelform&admin action

第二种方法更简单

1. 在bootstrap添加自定义的css样式文件,custom.css

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

2. 在基础模版(我定义的是base.html,其它html模块是继承它的)导入custom.css文件:

1
<link href="/static/bootstrap-3.3.7-dist/css/custom.css" rel="stylesheet">

3. 你随意在custom.css定义样式

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训 View Code

4. 在对应的customer.html的标签加入样式; customer.status是后台传给前端的,是学生的报名状态

1
<td class="{{ customer.status }}">{{ customer.get_status_display }}</td>

  

四、分页功能

其实Alex销售员登陆后看到的界面只有两条客户的信息,这是我在后台写的。注意看左下角有个分页,类似与百度搜索的分页。其实分页实现起来还是有点难度的。

先看django官方文档。官方文档写得很详细!!

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训 View Code

后台实现:

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

 1 def customers(request): 2     print(">>>>request:",request) 3     # 查找所有客户,获取所有信息的结果集,但并不是所有信息都已经取出来了(如果有上万条数据,不能一次性取出来,先取一部分), 4     customer_list = models.Customer.objects.all() 5     print(">>>>customers:", customer_list) 6     paginator = Paginator(customer_list, 2)  # 生成分页实例: 每一页有两条数据 7     page = request.GET.get("page")  # 获取前端点击的页数,参数page可自定义 8     try: 9         customer_objs = paginator.page(page)  # 生成第page页的对象10     except PageNotAnInteger:11         # If page is not an integer, deliver first page12         # 如果输入的页码不是下标,则返回第一页13         customer_objs = paginator.page(1)14     except EmptyPage:15         # If page is out of range (e.g. 9999), deliver last page of results.16         # 如果输入的页码超出,则跳转到最后的页码17         customer_objs = paginator.page(paginator.num_pages)18 19     return render(request, "crm/customer.html", {"customer_list": customer_objs})

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

前端实现:

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

 1 <div class="pagination"> 2  3         <nav> 4            <ul class="pagination"> 5                {% if customer_list.has_previous %} 6                     <li class=""><a href="?page={{ customer_list.previous_page_number }}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li> 7                {% endif %} 8  9                {% for page_num in customer_list.paginator.page_range %}10                     <!-- abs_page为函数名,后两个为参数 -->11                     {% abs_page customer_list.number page_num %}12 13                {% endfor %}14 15                {% if customer_list.has_next %}16                     <li class=""><a href="?page={{ customer_list.next_page_number }}" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>17                {% endif %}18            </ul>19         </nav>20 21     </div>

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

第一次进入http://127.0.0.1:8000/crm/customer/页面时,请求为get方式,后台接收到的page参数为空,故会出PagenotAnInterger异常,故会返回到第一页!!

注意前端的第九行代码: customer_list.paginator.page_range是页数的范围。customer_list只是一个第几页的实例而已,是无法获取到页数的范围的

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

但是问题来了,如果,你有100条数据,每页只放两条数据,意味着界面得有50个button,基本上页面是放不下的。

如果页面过多,看下百度怎么处理

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

可用abs绝对值,若当前页面为第6页,想让3、4、5和7、8、9也显示出来,可在循环判断页面时,利用abs, 当|循环的页面值-当前的页面值|<=3 ,则显示。
但问题又来了,前端的templates可没有abs取绝对值这种后台才有的方法,怎么办??

 

自定义template tags

https://docs.djangoproject.com/es/1.9/howto/custom-template-tags/ 

效果图:

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训  电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

后台是如何自定义模版??

首先自定义templates模版,我随便建了个文件custom_tags.py,必须放在新建包templatetags下

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

custom_tags.py: 当页码绝对值之差小于3时,则返回页码按钮的html给前端,反之不返回。

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

 1 from django import template 2 from django.utils.html import format_html 3   4 register = template.Library()  #django的语法库 5  6   7 @register.simple_tag 8 def abs_page(current_page, loop_page): 9     offset = abs(current_page - loop_page)10     if offset < 3:11         if current_page == loop_page:12             page_ele = "<li class='active'><a href='?page=%s'>%s</a></li>" % (current_page, current_page)13         else:14             page_ele = "<li class=''><a href='?page=%s'>%s</a></li>" % (loop_page, loop_page)15         return format_html(page_ele)  #将字符串转化为html,返回给前端16     else:17         return ""

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

 

五、modelform进阶

modelform之前有写过,django进阶-modelform&admin action, 但主要是写django自带的admin。

现在我有个需求,销售员Alex想查看客户的详细信息。只需只击客户的ID号,便可查看,当然也可以修改。

前端:

1
<td><a href="/crm/customers/{{customer.id}}/">{{customer.id}}</a></td>

urls:

1
2
# 当学员id当作参数,传给customer_detail方法
url(r'^customers/(\d+)/$', views.customer_detail),

后台:

1
2
3
4
def customer_detail(request,customer_id):
    customer_obj = models.Customer.objects.get(id=customer_id)
    form = forms.CustomerModelForm()
    return render(request,"crm/customer_detail.html",{"customer_form":form})

看前端界面显示: 虽然能显示出表单,但无法显示出学员的信息,而且太丑了!!

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

如何显示出学员的信息:

1
2
customer_obj = models.Customer.objects.get(id=customer_id)
form = forms.CustomerModelForm(instance=customer_obj)  # 将数据对象当作参数传入

如何使前端界面更漂亮:

forms.py表单文件:

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训 View Code

前端: 样式是从bootstrap参考来的

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训 View Code

效果图:

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

修改后保存信息

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训 View Code

 

六、必填与非必填字段

效果图: 必填字段有加粗,且左上角有红色*号

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

只需修改下前端代码即可:

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

1 {% if field.field.required %} <!--若是必填字段 -->2     <label class="col-sm-2 control-label">3         <span style="color: red;font-size: larger">*</span>{{ field.label }}4     </label>5 {% else %} <!-- label在django默认为加粗 -->6     <label style="font-weight: normal" class="col-sm-2 control-label">{{ field.label }}</label>7 {% endif %}

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

 

权限分配, 这个改天再写博客整理下: 三个角色的权限是不同的。对销售员来讲,无法修改非本人招收客户的信息。

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

 

七、url别名

啥是url别名??

1 #当学员id当作参数,传给customer_detail方法,2 #给该url起别名,一调用别名customer_detail,就关联上url3 url(r'^customers/(\d+)/$',views.customer_detail,name="customer_detail"),

现在销售员想查看客户的详细信息,只需一点击客户的ID号便可查看。so, ID号必须是个a标签,下面来看看前端实现:

1 <!-- 这里查看学员的详细信息不应该写列,否则当url一改变,得来这里改代码 -->2 <!-- <td><a href = " /crm/customers/ {{customer.id}} / "> {{ customer.id }} </a></td> -->3 4 <td><a href = "{% url  'customer_detail'  customer.id %}"> {{ customer.id }} </a></td>

注意了,如果不用url别名的话,就用第2行代码。但是,这样项目的可维护性大大降低了。当你需改动url时,必须到前端修改对应的a标签。如果用了url别名,就不用再来前端修改了。

电脑培训,计算机培训,平面设计培训,网页设计培训,美工培训,Web培训,Web前端开发培训

看到没,我用浏览器审查元素,浏览器已经自动将ID号的a标签,转化为一条url. 神奇!!

 

1.非系统的学习也是在浪费时间 2.做一个会欣赏美,懂艺术,会艺术的技术人


延伸阅读

云村系统个人感想-Java培训,做最负责任的教育,学习改变命运,软件学习,再就业,大学生如何就业,帮大学生找到好工作,lphotoshop培训,电脑培训,电脑维修培训,移动软件开发培训,网站设计培训,网站建设培训云村系统个人感想