单爆手

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
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>

 

posted on 2020-04-08 10:00  单爆手  阅读(300)  评论(0)    收藏  举报