CRM项目:
customer relationship management
需求分析
- 使用系统的人员
- 销售 财务 班主任 项目经理
- 需求分析:
- 注册
- 登陆
- 销售:
- 客户信息的管理
- 增加、编辑、查看客户信息
- 跟进记录的管理
- 增加、编辑、查看跟进记录
- 报名记录的管理
- 增加、编辑、查看报名记录
- 缴费的记录
- 增加、编辑、查看缴费记录
- 客户信息的管理
- 班主任:
- 班级的管理
- 增加、编辑、查看班级的信息
- 课程记录
- 增加、编辑、查看课程记录
- 学习记录表
- 增加、编辑、查看课程记录
- 班级的管理
表结构的设计
-
用户表
-
客户表
-
跟进记录表
-
报名表
-
缴费记录表
-
校区表
-
班级表
-
课程记录
-
学习记录表
-
下载 pip install django-multiselectfield
登录注册功能
-
views
from django.shortcuts import render,redirect,HttpResponse from crm import models import hashlib def login(request): if request.method == 'POST': user = request.POST.get('username') pwd = request.POST.get('password') md5 = hashlib.md5() #将密码加密验证 md5.update(pwd.encode('utf-8')) pwd = md5.hexdigest() obj = models.UserProfile.objects.filter(username=user,password=pwd,is_active=True).first() if obj: #obj查询没有结果为none return redirect('crm:customer') return render(request, 'login.html',{'error':'用户名密码错误'}) return render(request,'login.html') from django import forms #forms组件 from django.core.exceptions import ValidationError class RegForm(forms.ModelForm): password = forms.CharField(min_length=6,widget=forms.PasswordInput( attrs={'placeholder':'密码','autocomplete':'off'})) #自定义会覆盖默认的 re_password = forms.CharField(min_length=6,widget=forms.PasswordInput( attrs={'placeholder':'确认密码','autocomplete':'off'})) #新增字段 class Meta: #生成默认字段 model = models.UserProfile fields = '__all__' #所有字段在前端展示form组件,所有字段使用['username','password'] exclude = ['is_active'] #排除字段,生成时候没有,默认使用数据库中的默认值 widgets={ #默认字段对应的插件,添加属性 'username':forms.TextInput(attrs={'placeholder':'用户名','autocomplete':'off'}), 'password':forms.PasswordInput(attrs={'placeholder':'密码','autocomplete':'off'}), 'name':forms.TextInput(attrs={'placeholder':'真是姓名','autocomplete':'off'}), 'mobile':forms.TextInput(attrs={'placeholder':'手机号','autocomplete':'off'}), } error_messages={ 'username':{ 'unique':'用户名相同重新输入' } } def clean(self): #全局钩子 self._validate_unique = True #到数据库效验唯一性,前端会展示数据已经存错误 password = self.cleaned_data.get('password') re_password = self.cleaned_data.get('re_password') #判断两次密码是否一致,是将数据返回,and前面为判断是否为空 if password and password == re_password: md5 = hashlib.md5() md5.update(password.encode('utf-8')) self.cleaned_data['password'] = md5.hexdigest() #将密码修改为加密后的 return self.cleaned_data self.add_error('re_password','两次密码不一致!') #加入到指定字段显示错误 raise ValidationError('两次密码不一致') def reg(request): #注册账号 form_obj = RegForm() #实例化对象 if request.method == 'POST': form_obj = RegForm(request.POST) # print(form_obj) if form_obj.is_valid(): #获取form表单进行效验 form_obj.save() return redirect('crm:login') #数据写入后完,跳转至登录页面 # print(form_obj.cleaned_data) return render(request,'reg.html',{'form_obj':form_obj})
-
login.html
<form action="" method="post"> {% csrf_token %} <div> <input type="text" name="username" class="username" placeholder="输入账号" autocomplete="off"> </div> <div> <input type="password" name="password" class="password" placeholder="输入密码" oncontextmenu="return false" onpaste="return false"> </div> <div>{{ error }}</div> <button id="submit" >登录</button> </form>
展示数据:
-
展示方法
-
普通字段
#对象.字段名 ——》 数据库的值 <td>{{ customer.qq }}</td>
-
choices
#对象.字段名 ——》 数据库的值 #对象.get_字段名_display() ——》 对应显示的值 <td>{{ customer.get_source_display }}</td>
-
外键
#对象.外键 ——》 关联的对象 __str__ #对象.外键.字段 <td>{{ customer.consultant.name }}</td> #consultant外键
-
多对多
<td>{% for foo in customer.class_list.all %} {{ foo.get_course_display }} {% endfor %} </td>
-
自定义model方法
from django.utils.safestring import mark_safe #前端html不转义 #前端调用: <td>{{ customer.show_status }}</td> #models def show_status(self): color_dict = { 'signed': 'green', 'unregistered':'red', 'studying': 'pink', 'paid_in_full':'gold' } return mark_safe(f'<span style="color: white;background-color: {color_dict.get(self.status)};padding: 3px">{self.get_status_display()}</span>')
模糊查询
Q((Q(qq__contains=query) | Q(name__contains=query) ) q = Q() q.connector = 'OR' q.children.append(Q(qq__contains=query)) q.children.append(Q(name__contains=query)) Q(qq__contains=query) Q(('qq__contains',query))
分页的问题
request.GET QueryDict 默认不可修改 request.GET._mutable = True request.GET['page'] = 1 QueryDict(mutable=True) 可修改的字典 request.GET.copy() 深拷贝 可修改 request.GET.urlencode() {query:123,page:2} _> query=123&page=2
编辑后跳转到原页面
生成url地址
@register.simple_tag def url_tag(request,name,*args,**kwargs): url = reverse(name,args=args,kwargs=kwargs) next = request.get_full_path() qd = QueryDict(mutable=True) qd['next']=next return "{}?{}".format(url,qd.urlencode())
编辑保存后跳转到原页面
next = request.GET.get('next') if next: return redirect(next) return redirect('crm:customer_list')
-
权限
-
用户登录成功,查询权限信息,也就是查询权限路径
#去空去重 permissions = user_obj.roles.filter(permissions__url__isnull=False).values('permissions__url').distinct() #user_obj.roles permissions__url #获取到的是当前登录成功的用户对象,通过用户对象,找到用户角色,在用双下方法查找当前url权限有多少
-
将路径信息保存在session中
#query_set默认不可json序列化 request.session['permissions'] = list(permissions) #登录状态保存在session中 request.session['is_login'] = True #保存登录状态
-
获取页面路径,进行效验:
class RbacMiddleWare(MiddlewareMixin): def process_request(self,request): url = request.path_info #获取当前访问的地址 for i in settings.WHITE_LIST: #白名单校验,判断url中是否含有login if re.match(i,url): #i正则匹配url是否跟自己匹配 return is_login = request.session.get('is_login') #没有登录状态,重新登录 if not is_login: return redirect('login') for i in settings.PASS_AUTH_LIST: #免认证登录,不需要权限,如首页 if re.match(i,url): return permissions = request.session.get('permissions') #获取权限信息 for i in permissions: #判断访问的地址是否跟权限中地址匹配成功 if re.match(r'^{}$'.format(i['permissions__url']), url): return return HttpResponse('没有访问权限,练习管理员') #效验全部不通过,拒绝请求