1. 回顾: 1. 表结构设计 2. 表结构设计的常见问题 --> 《漫画数据库》 --> 数据库设计三大范式 3. 职场发邮件 4. CRM表结构分析 --> 每个表的字段搞清楚(一对一,一对多,多对多) ralated_name='反向查询的字段' 回去复习下ORM 5. 登录 1. auth模块 2. session操作 request.session.set_expiry(0) --> 设置关闭浏览器session就失效 request.session.set_expiry(7*24*60*60) --> 设置7天后失效(7天登录) 回去复习下session
2. 内容: 1. ModelForm 1. ModelForm是什么 1. 将Form和Model结合起来使用 2. ModelForm的好处 1. 省去了自己写Form字段 2. 更新的时候设置instancec桉树,然后直接调用.save()方法即可实现更新 3. ModelForm的使用: 1. class Meta下面的配置 1. model = 'ORM类名' #我这个form和哪一个models中表类结合 2. 字段展示(用到哪些字段用法): 1. fields = '__all__' 2. fields = ['要展示的字段1', '字段2'] 3. exclude = ['需要排除的字段1', ...] 3. labels = {#这是给所有字段统一配置labels '字段名': 'label名' } 4. error_messages = {#字段错误提示,每一个字段配置小字典 '字段名': { 'min_length': '最小长度不能小于xx位', 'max_length': '最大长度不能超过xx位', 'required': '这个字段是必填项' } } 5. widgets = {#插件--把每一个字段都要写在统一放在此字典中 'password': forms.widgets.PasswordInput() } 2. 校验validators认证校验器: 1. 可以使用model中字段的validators配置 2. 也可在自己在Form类中重写字段,定义validators配置如下:
email = forms.CharField(
validators=[RegexValidator(r"[1-9][0-9]{4,12}@qq\.com", "请输入正确的qq邮箱 heihiehie"), ]
)
3. 局部钩子方法和全局钩子方法 同Form的用法
一.注册组件之ModelForm版注册1:
modelform是和模型关联的form,即我所有的字段是根据模型model来建的(模型里有什么字段我就用什么字段)。这样好处是自定form组件是不用在form里挨个写字段了,因为你现在已经与model.py中的**表类建立一对一关系了,相当于在现在的forms.py中的写的数据已经直接写在models.py中了。
(1)crm/forms.py(注册的form验证组件):
你定义modelform时,相当于你所有的字段都是按照orm中的类来的,所以你得告诉它关联哪个类,此时这个form.py就和UserProfile这个表类就关联起来了(如下中).且在其中可指定显示哪些字段以及添加自定义字段
from django import forms from crm.models import UserProfile from django.core.exceptions import ValidationError #检验错误 class RegForm(forms.ModelForm): re_password = forms.CharField( #因为models.py中没有确认密码字段,但可在form表单中自定义字段 label = '确认密码', widget=forms.widgets.PasswordInput() ) class Meta:#类的配置信息 model = UserProfile #你的forms和UserProfile这个表类关联起来 #fields = '__all__' #展示所有字段 fields = ['email','password','re_password','name','mobile'] #展示指定字段 # exclude = ['']#甩不需要展示的字段排除 labels = { #把显示英文的字段名改成中文名只要把它们写在此labels大字典中即可 'email':'邮箱', } error_messages = { #把某字段要报错的提示信息放在此大字典中即可 'password':{ 'min-length':'密码8位', } } widgets = {#自定制某字段的校验规则可写在此大字典中 'password': forms.widgets.PasswordInput(),#密文 're_password': forms.widgets.PasswordInput() } def __init__(self,*args,**kwargs):#重写它的__init__方法,这样可同时给上面多个字段加样式 super().__init__(*args,**kwargs) #给form实例对象的每一个字段添加class--就是for循环它的每一个字段对象,并update一下 for field in self.fields.values(): field.widget.attrs.update({'class':'form_control'}) #给每一个字段对象加上form_control样式了 #认证:用勾子如下两个方法 def clean_email(self):#检验邮箱是否被注册 email_value = self.cleaned_data.get('email') is_exist = UserProfile.objects.filter(email=email_value) #从UserProfile表中过滤 if is_exist: raise ValidationError('邮箱已被注册') else: return email_value def clean(self):#校验密码与确认密码是否一致 pwd_value = self.cleaned_data.get('password') re_pwd_value = self.cleaned_data.get('re_password') if pwd_value == re_pwd_value: return self.cleaned_data else: self.add_error('re_password','两次密码不一致') raise ValidationError('两次密码不一致')#抛出异常
(2)urls.py中:
from django.conf.urls import url from django.contrib import admin from crm import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/', views.LoginView.as_view()), url(r'^index/', views.index), url(r'^reg/', views.RegView.as_view()), ]
(3)views.py中:
from django.shortcuts import render, redirect, HttpResponse
from django import views
from django.contrib import auth
from django.contrib.auth.decorators import login_required
from crm.forms import RegForm #导入我自己写的forms文件
class LoginView(views.View):
def get(self, request):
return render(request,'login.html')
def post(self,request):
# 1. 获取用户输入的内容
email = request.POST.get('email')
pwd = request.POST.get('password')
is_check = (request.POST.get('is_check',None) == '777')
# 2. 校验用户名密码是否正确
user_obj = auth.authenticate(request, email=email, password=pwd)
if user_obj:
# 登录成功
# 存Session数据并且回写Cookie
auth.login(request, user_obj) # auth认证中间件的源码
if is_check:
request.session.set_expiry(7*24*60*60)
else:
request.session.set_expiry(0)
# 跳转到首页
return redirect('/index/')
else:
# 登录失败
return render(request, 'login.html', {'error_msg': '邮箱或密码错误'})
@login_required
def index(request):
return HttpResponse('不错')
class RegView(views.View):
def get(self,request):
form_obj = RegForm() #实例化
return render(request,'reg.html',{'form_obj':form_obj}) #把想展示的字段传到前端
def post(self,request):
form_obj = RegForm(request.POST)
if form_obj.is_valid():
# 校验通过
# 方法一:
# 1. 先把re_password字段去掉
# form_obj.cleaned_data.pop('re_password')
# # 2. 去数据库创建新用户
# UserProfile.objects.create_user(**form_obj.cleaned_data)
# 方法二:现在的form_obj是一个ModelForm对象而不是普通form了,它和数据库里面的Model类是对应的
user_obj = form_obj.save() #这样就直接能在数据库中创建用户对象 -->相当于执行了 UserProfile.objects.create(**form_obj.cleaned_data)
user_obj.set_password(user_obj.password)#设成密文保存
user_obj.save()
return redirect('/login/')
else:
return render(request, 'reg.html', {'form_obj': form_obj})
(4)templates/reg.html中:
注册页面写了for循环每一字段,所以在后端要给它传字段对象(views中要实例化我自己在forms.py中写的RegView类),并把它传到前端reg.html中
但其实我注册时不需要这么多字段,所以我可以在modlesform中指定展示哪些字段(forms.py中)。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>注册</title>
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7/css/bootstrap.css' %}">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h1>欢迎注册</h1>
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form_obj %} {#循环所有字段并有div把它包起来显示 #}
<div>
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
<span class="help-block">{{ field.errors.0 }}</span>
</div>
{% endfor %}
<input type="submit">
</form>
</div>
</div>
</div>
<script src="{% static 'jquery.js' %}"></script>
<script src="{% static 'bootstrap-3.3.7/js/bootstrap.js' %}"></script>
</body>
</html>
(5)models.py中:
可直接在表类中定义某字段的校验规则了如正则,因为用的是modelform.
from django.core.validators import RegexValidator #校验 ....... class UserProfile(AbstractBaseUser, PermissionsMixin): email = models.EmailField( max_length=255, unique=True, validators=[RegexValidator(r"[1-9][0-9]{4,12}@qq\.com", "请输入正确的qq邮箱 heihiehie"), ]#这就是给此字段加的校验规则 ) ......
最终效果如下点提交:
二.展示客户数据
2. CRM客户信息展示 1. Django admin的简单使用(即用图开化web页面去管理数据库添加数据)
1. 如何注册 2. 几个配置项 1. verbose_name --> Django admin管理后台展示的内容 2. blank=True --> Django admin管理后台中该字段可以不填 2. 客户信息展示页面 1. 展示带choice选项的内容 get_字段名_display() get_字段名_display --> 前端不加括号 2. 在ORM的类中定义方法
1.超级用户登进去后如下图发现有默认的组,所以我应该把我的customer这张客户表放到其中(就是让django 的admin来管理我自己写的表),所以得注册下,告诉django有哪些表要管理.
(1)crm/admin.py中:效果如下图有了,并让它显示中文。
from django.contrib import admin from crm.models import Customer #注册自己写的表(model类) admin.site.register(Customer)
(2)models.py中:customer表中添加如下代码:
class Meta: verbose_name = '客户' verbose_name_plural = verbose_name#复数让其不在页面中中文后加英文字
一点客户后增加就跳到如下页面了,在其中可直接添加数据就能到数据库中了:
同上述把班级ClassList表和校区Campuses表也注册并显示中文!!
如下图我给它添加三个校区后页面显示如下这样,所以得在model.py中给校区表加__str__方法就可同样把班级表和Userprofile表也加__str__方法并返回self.name:
def __str__(self): return self.name
班级表特殊要如下写:做拼接:
def __str__(self):#三个都得一起返回,因为联合唯一。且course字段有choices对象所以用特殊方法get_字段_display() return '{}--{}({})'.format(self.get_course_display(), self.semester, self.campuses)
用客户表如下写做拼接:
def __str__(self): return '{}--{}'.format(self.qq,self.name)
并且让如下字段设置成可以不填为空
class_list = models.ManyToManyField('ClassList', verbose_name="已报班级", null=True, blank=True)
(1)knight/urls.py中:
二级路由应用(项目中app应用多时用),如下中所有以crm开头的url都交给二级路由来处理
from django.conf.urls import url,include from django.contrib import admin from crm import views from crm import crm_urls urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/', views.LoginView.as_view()), url(r'^index/', views.index), url(r'^reg/', views.RegView.as_view()), url(r'^crm/',include(crm_urls)), ]
(2)crm/crm_urls.py中:
""" crm app路由匹配规则 所有以crm开头的请求都会转到这里来做后续的匹配 """ from django.conf.urls import url from crm import views urlpatterns = [ url(r'customer_list/$', views.customer_list),
]
(3)views.py中:
def customer_list(request): #取到所有的客户信息并 data = Customer.objects.all() #在页面上展示出来 return render(request,'customer_list.html',{'customer_list':data})
三客户页面创建:
1.templates/customer_list.html:
导入bootstrap模版后页面效果如下:
(1).(去掉不需要的部分)并把bootstrap中的nav导航条(组件)把此组件复制到templates/nav.html---自建组件,这样customer_list.html中include一下就可以用到此组件了,并把此组件固定在顶部并变成黑色。并把此组件(导航条)固定在顶部且呈黑色。
(2).员工登录后展示名字等还有下拉框.注销,注册等
(3).并根据用户是否登录做判断不同显示:只要你用auth.login认证操作后,你session认证不过期,那你请求再来访问我网站时,request.user就能拿到当前登录的用户对象并能拿到用户名字,若没登录则返回匿名用户都是null,所以我用if判断
(4).注销功能:点注销时跳转到登录页面
此时再访问http://127.0.0.1:8000/crm/customer_list/则导航条右侧就只显示登录和注册了
(5)怎样确保登访问客户页面时是必须登录的?---:在views.py中给客户展示方法加@login_required装饰器即可了。此时你若没登录那怎么走哪url都是跳到登录页面
(6)导航条登录后右侧待办事项后我想标注识个未读消息的符号怎么做?--bootstrap中有个徽章组件
<span class="badge">4</span>把这个直接回到nav组件的待办事项后即可。
(7)更改标题和左边菜单栏字,中间面板只留表格customer_list.html页面中修改即可
(8)客户列表表格中有哪些字段?--customer_list.html中修改后下图变成下下图效果了:
![]()
2.1对客户列表字段做展示修改:
但是上图效果中性别字段展示英文,因为在models.py数据库customer表中sex字段写了choices,所 以要想在模版语言里展示这种带choice的字段就用get_字段名_display方法即可(前端中不用加括号)。
并且电话没有的给它弄成暂无字样
客户来源也给它写中文:
班级类型也给它改成中文:
状态也一样:
咨询日期给它格式化成年-月-日 时间:也可用如下法直接在settings.py中配成全局的
USE_L10N = False#是否本地化
DATE_FORMAT = 'Y-m-d'
DATETIME_FORMAT = 'Y-m-d H:i:s'
2.2已报班级:它是manytomany,那怎么展示才好?--ORM中自定义方法
因为models.py中customer客户列表是一个类class,而class中除了能够设置属性还可设置方法--自定义类的方法,而customer这个类的对象可以调用此方法,但是我customer_list.html中页面客户表中那些customer是类的对象,所以它可调用自定的方法.
modles.py中customer类中追加如下方法即可:
#自定义类的方法
def show_class_list(self):#self是表示当前这个客户对象,所以self.claa_list.all()拿到的是所有和我关联的班级
return '|'.join([str(i) for i in self.class_list.all()]) #列表生成式拿到迭代中所有的班级对象,并str(对象)就变成字符串了,并用管道符拼接
3.把报名和未报名的用不同颜色标识下--在models.py中再自定义方法展示不同状态不同色--这样在前面页面模版中直接调用此方法就可了
models.py中customer类中追加此方法:
from django.utils.safestring import mark_safe #转义--把返回的html的字符串保证它是安全的 # 展示状态 def show_status(self):#直接返回一个span标签 _status_color = { 'signed': 'blue', #状态名是key 'unregistered': 'red', 'studying': 'orange', 'paid_in_full': 'green', } #此字典是把下面的color前的{}做成字符串格式化替换不同的颜色 return mark_safe('<span style="background-color: {};color: white">{}</span>'.format( _status_color[self.status],#这是拿到字典中的第一个值即key并放在第一个{}中 self.get_status_display() #这是拿到字典中的值并放在第二个{}中即显示的文本 ))
或不用mark_safe方法转义也行,直接在customer_list.html中用safe即可如下:
<td>{{ customer.show_status|safe }}</td>
最终效果如下:
四.最终修改的文件中代码如下:
(1)crm/urls.py中定义注销:
url(r'^logout/', views.logout),
(2)views.py中:
def logout(request): auth.logout(request) #auth模块中.logout方法中只把request对象传进去,这个logout就会把你请求中的session和cokie设置成过期的,下次再访问就没了 return redirect('/login/')
(3)templates/customer_list.html:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content=""> <link rel="icon" href="https://v3.bootcss.com/favicon.ico"> <title>客户展示页面</title> {% load static %} <!-- Bootstrap core CSS --> <link href="{% static 'bootstrap-3.3.7/css/bootstrap.css' %}" rel="stylesheet"> <!-- Custom styles for this template --> <link href="{% static 'css/dashboard.css' %}" rel="stylesheet"> </head> <body> {% include 'nav.html' %} {# 导入nav组件 #} <div class="container-fluid"> <div class="row"> <div class="col-sm-3 col-md-2 sidebar"> <ul class="nav nav-sidebar"> <li class="active"><a href="/crm/customer_list/">客户列表</a> </li> <li><a href="https://v3.bootcss.com/examples/dashboard/#">Reports</a></li> <li><a href="https://v3.bootcss.com/examples/dashboard/#">Analytics</a></li> <li><a href="https://v3.bootcss.com/examples/dashboard/#">Export</a></li> </ul> </div> <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"> <h2 class="sub-header">客户列表</h2> <div class="table-responsive"> <table class="table table-striped"> <thead> <tr> <th>#</th> <th>QQ</th> <th>QQ昵称</th> <th>姓名</th> <th>性别</th> <th>电话</th> <th>客户来源</th> <th>咨询课程</th> <th>班级类型</th> <th>状态</th> <th>咨询日期</th> <th>已报班级</th> </tr> </thead> <tbody> {% for customer in customer_list %}{# 写for循环挨个展示上述这些字段即可 #} <tr> <td>{{ forloop.counter }}</td> <td>{{ customer.qq }}</td> <td>{{ customer.qq_name }}</td> <td>{{ customer.name }}</td> <td>{{ customer.get_sex_display }}</td> <td>{{ customer.phone|default:"暂无" }}</td> {# 当pthon没有值时显示暂无 #} <td>{{ customer.get_source_display }}</td> <td>{{ customer.course }}</td> <td>{{ customer.get_class_type_display }}</td> <td>{{ customer.show_status }}</td> <td>{{ customer.date|date:"Y-m-d H:i:s" }}</td> {# 给日期格式化成此格式 #} <td>{{ customer.show_class_list }}</td> </tr> {% endfor %} </tbody> </table> </div> </div> </div> </div> <!-- Bootstrap core JavaScript ================================================== --> <!-- Placed at the end of the document so the pages load faster --> <script src="{% static 'jquery.js' %}"></script> <script src="{% static 'bootstrap-3.3.7/js/bootstrap.js' %}"></script> </body> </html>