zoukankan      html  css  js  c++  java
  • python2.0_day18_Django自带的用户认证模块的使用

    用户验证
    我们基于一个项目来学习利用Django框架中的user表实现用户认证
    Django练习小项目:学员管理系统设计开发
    项目需求:
    1.分讲师学员课程顾问角色,
    2.学员可以属于多个班级,学员成绩按课程分别统计
    3.每个班级至少包含一个或多个讲师
    4.一个学员要有状态转化的过程 ,比如未报名前,报名后,毕业老学员
    5.客户要有咨询纪录, 后续的定期跟踪纪录也要保存
    6.每个学员的所有上课出勤情况学习成绩都要保存
    7.学校可以有分校区,默认每个校区的员工只能查看和管理自己校区的学员
    8.客户咨询要区分来源


    学员管理系统表结构
      1 #_*_coding:utf-8_*_
      2 from django.db import models
      3 
      4 # Create your models here.
      5 from django.core.exceptions import ValidationError
      6 
      7 from django.db import models
      8 from django.contrib.auth.models import User
      9 
     10 class_type_choices= (('online',u'网络班'),
     11                      ('offline_weekend',u'面授班(周末)',),
     12                      ('offline_fulltime',u'面授班(脱产)',),
     13                      )
     14 class UserProfile(models.Model):
     15     user = models.OneToOneField(User)
     16     name = models.CharField(u"姓名",max_length=32)
     17     def __unicode__(self):
     18         return self.name
     19 
     20 
     21 class School(models.Model):
     22     name = models.CharField(u"校区名称",max_length=64,unique=True)
     23     addr = models.CharField(u"地址",max_length=128)
     24     staffs = models.ManyToManyField('UserProfile',blank=True)
     25     def __unicode__(self):
     26         return self.name
     27 
     28 
     29 class Course(models.Model):
     30     name = models.CharField(u"课程名称",max_length=128,unique=True)
     31     price = models.IntegerField(u"面授价格")
     32     online_price = models.IntegerField(u"网络班价格")
     33     brief = models.TextField(u"课程简介")
     34     def __unicode__(self):
     35         return self.name
     36 
     37 
     38 class ClassList(models.Model):
     39     course = models.ForeignKey('Course')
     40     course_type = models.CharField(u"课程类型",choices=class_type_choices,max_length=32)
     41     semester = models.IntegerField(u"学期")
     42     start_date = models.DateField(u"开班日期")
     43     graduate_date = models.DateField(u"结业日期",blank=True,null=True)
     44     teachers = models.ManyToManyField(UserProfile,verbose_name=u"讲师")
     45 
     46 
     47     #def __unicode__(self):
     48     #    return "%s(%s)" %(self.course.name,self.course_type)
     49 
     50     class Meta:
     51         verbose_name = u'班级列表'
     52         verbose_name_plural = u"班级列表"
     53         unique_together = ("course","course_type","semester")
     54 
     55 
     56 class Customer(models.Model):
     57     qq = models.CharField(u"QQ号",max_length=64,unique=True)
     58     name = models.CharField(u"姓名",max_length=32,blank=True,null=True)
     59     phone = models.BigIntegerField(u'手机号',blank=True,null=True)
     60     stu_id = models.CharField(u"学号",blank=True,null=True,max_length=64)
     61     #id = models.CharField(u"身份证号",blank=True,null=True,max_length=128)
     62     source_type = (('qq',u"qq群"),
     63                    ('referral',u"内部转介绍"),
     64                    ('51cto',u"51cto"),
     65                    ('agent',u"招生代理"),
     66                    ('others',u"其它"),
     67                    )
     68     source = models.CharField(u'客户来源',max_length=64, choices=source_type,default='qq')
     69     referral_from = models.ForeignKey('self',verbose_name=u"转介绍自学员",help_text=u"若此客户是转介绍自内部学员,请在此处选择内部学员姓名",blank=True,null=True,related_name="internal_referral")
     70     # 我们知道一般在表中创建外键关联字段时,ForeignKey()外键关联的都是其他表,不是本表.
     71     # 本项目中我们存在一个业务是需要关联本表的需求: 学员可以转介绍学员.
     72     # 那么如何实现呢,就是在 ForeignKey('self'),verbose_name是在admin后台显示的字段名称.help_text就是提示信息.
     73     # related_name这个注意了:只要是关联自己就必须有这个属性设置(因为当用forekey()关联其他表时,反向查询常规方法,而这种关联本表的foreignKey()反向查询就用related_name定义的值,这里就是internal_referral,用的时候作为字段使用)
     74     # 比如,我要查张三介绍了的几个学员 '张三'.internal_referral
     75 
     76     course = models.ForeignKey(Course,verbose_name=u"咨询课程")
     77     class_type = models.CharField(u"班级类型",max_length=64,choices=class_type_choices)
     78     customer_note = models.TextField(u"客户咨询内容详情",help_text=u"客户咨询的大概情况,客户个人信息备注等...")
     79     status_choices = (('signed',u"已报名"),
     80                       ('unregistered',u"未报名"),
     81                       ('graduated',u"已毕业"),
     82                       )
     83 
     84     status = models.CharField(u"状态",choices=status_choices,max_length=64,default=u"unregistered",help_text=u"选择客户此时的状态")
     85     consultant = models.ForeignKey(UserProfile,verbose_name=u"课程顾问")
     86     date = models.DateField(u"咨询日期",auto_now_add=True)
     87 
     88     class_list = models.ManyToManyField('ClassList',verbose_name=u"已报班级",blank=True)
     89 
     90     def __unicode__(self):
     91         return "%s,%s" %(self.qq,self.name )
     92 
     93 
     94 
     95 class ConsultRecord(models.Model):
     96     customer = models.ForeignKey(Customer,verbose_name=u"所咨询客户")
     97     note = models.TextField(u"跟进内容...")
     98     status_choices = ((1,u"近期无报名计划"),
     99                       (2,u"2个月内报名"),
    100                       (3,u"1个月内报名"),
    101                       (4,u"2周内报名"),
    102                       (5,u"1周内报名"),
    103                       (6,u"2天内报名"),
    104                       (7,u"已报名"),
    105                       )
    106     status = models.IntegerField(u"状态",choices=status_choices,help_text=u"选择客户此时的状态")
    107 
    108     consultant = models.ForeignKey(UserProfile,verbose_name=u"跟踪人")
    109     date = models.DateField(u"跟进日期",auto_now_add=True)
    110 
    111     def __unicode__(self):
    112         return u"%s, %s" %(self.customer,self.status)
    113 
    114     class Meta:
    115         verbose_name = u'客户咨询跟进记录'
    116         verbose_name_plural = u"客户咨询跟进记录"
    117 
    118 
    119 
    120 class CourseRecord(models.Model):
    121     course = models.ForeignKey(ClassList,verbose_name=u"班级(课程)")
    122     day_num = models.IntegerField(u"节次",help_text=u"此处填写第几节课或第几天课程...,必须为数字")
    123     date = models.DateField(auto_now_add=True,verbose_name=u"上课日期")
    124     teacher = models.ForeignKey(UserProfile,verbose_name=u"讲师")
    125     def __unicode__(self):
    126         return u"%s 第%s天" %(self.course,self.day_num)
    127     class Meta:
    128         verbose_name = u'上课纪录'
    129         verbose_name_plural = u"上课纪录"
    130         unique_together = ('course','day_num')
    131 
    132 
    133 class StudyRecord(models.Model):
    134     course_record = models.ForeignKey(CourseRecord, verbose_name=u"第几天课程")
    135     student = models.ForeignKey(Customer,verbose_name=u"学员")
    136     record_choices = (('checked', u"已签到"),
    137                       ('late',u"迟到"),
    138                       ('noshow',u"缺勤"),
    139                       ('leave_early',u"早退"),
    140                       )
    141     record = models.CharField(u"上课纪录",choices=record_choices,default="checked",max_length=64)
    142     score_choices = ((100, 'A+'),
    143                      (90,'A'),
    144                      (85,'B+'),
    145                      (80,'B'),
    146                      (70,'B-'),
    147                      (60,'C+'),
    148                      (50,'C'),
    149                      (40,'C-'),
    150                      (0,'D'),
    151                      (-1,'N/A'),
    152                      (-100,'COPY'),
    153                      (-1000,'FAIL'),
    154                      )
    155     score = models.IntegerField(u"本节成绩",choices=score_choices,default=-1)
    156     date = models.DateTimeField(auto_now_add=True)
    157     note = models.CharField(u"备注",max_length=255,blank=True,null=True)
    158 
    159     def __unicode__(self):
    160         return u"%s,学员:%s,纪录:%s, 成绩:%s" %(self.course_record,self.student.name,self.record,self.get_score_display())
    161 
    162     class Meta:
    163         verbose_name = u'学员学习纪录'
    164         verbose_name_plural = u"学员学习纪录"
    165         unique_together = ('course_record','student')
    学员公里系统表结构

    我们首先创建一个新的Django project项目
    1  django-admin startproject  s12day18stu_crm
    我们知道使用命令创建project时,默认是不会创建templates目录的,所以这里我们使用命令创建这个目录用于存放html模版文件
    创建一个app,用来开发学员管理系统项目
    1  python3.5 manage.py startapp app01
    先把表结构拷贝进来.
    首先我们看
    1 from django.contrib.auth.models import User
    2 class UserProfile(models.Model):
    3     user = models.OneToOneField(User)
    4     name = models.CharField(u"姓名",max_length=32)
    5     def __unicode__(self):
    6         return self.name
    user字段用的是models.OneToOneField(User),这里的User是Django中用户验证模块
    OneToOneField
    这里user = models.OneToOneField(User),相当于做了一个外键,只是一般外键是1对多的,但是这里是1对1的,为什么1对1呢?
    你想如果是普通的外键,假如有两个账户Alex1和Alex2 都外连同一个外键,那么问当我们用django中的这个账户登录时,到底登录到哪个账户呢?所以这里必须1对1,
    mysql中是不能实现限制1对1的功能.所以这里OneToOneField()其实是在Django中实现的限制的.
    那么我们为什么创建一个userprofile()还要用Django中的User表做外键呢?直接定义用户登录名和密码不就行了吗?问的好!
    session的简单需求介绍
    1.首先浏览器访问网页的连接属于短链接(非点击一次链接请求,服务器返回请求内容,然后就断开了),在点一次就属于心的请求.
    2.我们知道当你访问一个网站,比如京东,加入你在登录界面输入用户名和密码后,成功登录到系统.
    2. 当你在购买物品后,点击支付,这时候把请求发给服务端,服务端这时候接收到请求后,对于服务端只是一个请求,支付的前提是你已经是登录后的账户,但是服务器怎么知道?
    基本上所有网站都是通过session来实现身份标记的.当你登录到网站后,服务端就会生成一个session(一个字符串),服务端在返回请求时把session返回给请求端.
    那么这样一个生成session的过程,是需要我们进行代码实现的.所以我们使用Django中user模块,在验证的时候就实现了session的生成,
    并且user模块不仅实现了生成session,同时还实现了密码的加盐加密.(加盐加密就是在md5加密的基础上在加一些我们自己的特定字符串.)

    上面OneToOneField 和 session的知识点内容介绍完后,我们就可以创建这个app01这个应用的数据库了.

    1. 首先创建一个s12day18stu_crm库(因为我们创建Django Project的时候的名字是s12day18stu_crm),其中要指定数据库的字符编码
    create database s12day18stu_crm default CHARSET utf8;
    2. 然周执行python3.5 manage.py migrate,第一次执行时不需要生成配置文件,因为默认是添加Django里的admin和auth表,差不多有9张表把.
    3. 把app01 添加到settings.py文件里
    4. 生成新的配置文件python3.5 manage.py makemigrations
    5. 创建app01程序数据库

    这些步骤都完成后,我们可以在数据库中查看到app01_开头的表,和Django后台系统使用的auth_ 开头的以及Django_开头的表
    6. 开启Django服务
    python3.5 manage.py runserver 127.0.0.1:8000
    7. http://127.0.0.1:8000/admin访问后台,提示输入用户名和密码
    8. 创建用户名和密码python3.5 manage.py createsuperuser
    9. 把app01程序下的数据库表加到admin后台管理
     1 from django.contrib import admin
     2     # Register your models here.
     3     from app01 import models
     4     admin.site.register(models.Customer)        # 客户表(学员表)
     5     admin.site.register(models.ConsultRecord)   #客户咨询跟进记录表
     6     admin.site.register(models.ClassList)   #班级表
     7     admin.site.register(models.CourseRecord) # 班级上课记录表
     8     admin.site.register(models.StudyRecord)  # 学员学习记录表
     9     admin.site.register(models.UserProfile)  #用户表
    10     admin.site.register(models.Course)   #课程表

    通过后台管理数据库
    这里有两个点需要测试:
    1. 客户表创建客户时,学员转介绍 选择本表里的其他学员
    2. 班级上课记录表是班级与第几节课以及类型(网络面授)联合唯一.然后学员学习在关联班级上课记录表,如果想知道某一条班级上课记录表有多少学生上课怎么办?
    解决思路是,首先从班级上课记录表查 学员学习记录的条目是反向关联.然后我们就应该我们应该查询应该是学员学习记录表,且筛选条件为班级上课记录,这里应该定义一个Django admin后台的action动作 初始化 该班级下的所有学员创建一条新课程的学员学习记录.
    然后在查看学员学习记录时应该通过帅选filter选择某一条班级课程记录.

    学习视频中,老师实现了,后台管理界面中首先按照学校分类,点进去后才是 客户表客户咨询跟进记录表班级表....课程表.
    但是视频中如何实现未看到,需要找高人指点.

    紧接着学习的知识点:用户认证的实现
    我们先给app01写一个首页:
    1.在urls.py写一个URL
    1         from django.conf.urls import url
    2         from django.contrib import admin
    3         from app01 import views
    4 
    5         urlpatterns = [
    6             url(r'^admin/', admin.site.urls),
    7             url(r'^$', views.index),
    8         ]
        2.在views里定义index
    1         def index(request):
    2             return render(request,'index.html')
        3.在templates目录下创建index.html
    1         <body>
    2             Welcome To Oldboy CRM
    3         </body>
        4. 访问http://127.0.0.1:8000查看访问结果
    OK的
    那么怎么实现只有登录后才能看到这个页面?
    如果有N多个页面都需是需要登录后才能访问的,我们应该在每一个视图里都加一个if else判断,判断的内容是request里的session内容是不是和服务器一直(当然前提还得写一个登录后生成session的函数).
    简单点的方法是就写一个验证函数,然后在视图里,每一个函数前面加一个装饰器.
    实现思路就是这样,而在于Django中,这个装饰器Django已经写好了.我们直接调用就可以了

    5.更改views.py文件,加入验证的装饰器
    1         from django.shortcuts import render
    2         from django.contrib.auth.decorators import login_required  #引入登录验证模块
    3 
    4         # Create your views here.
    5         @login_required
    6         def index(request):
    7             return render(request,'index.html')
    然后登录测试:

             

        结果:我们访问的是http://127.0.0.1:8000却被转到:http://127.0.0.1:8000/accounts/login/?next=/
    这是因为我们没登录,Django默认把URL转到这个URL,所以我们应该在这个URL里写一个登录页面
    6.写一个登录页面,URL为:http://127.0.0.1:8000/accounts/login/
    添加urls
    1         urlpatterns = [
    2             url(r'^admin/', admin.site.urls),
    3             url(r'^$', views.index),
    4             url(r'^accounts/login/$', views.acc_login),
    5         ]
            编辑app01/views.py
    1         def acc_login(request):
    2             return render(request,'login.html')
            创建templates/login.html
    我们先看index.html
    1             <body>
    2             <h1>oldboy CRM</h1>
    3             {% block page-container%}
    4                 Welcome To Oldboy CRM
    5             {% endblock %}
    6             </body>
            我们创建login.html直接让它即成index.html,然后在重新block里的内容
    查看login.html内容如下:
    1             {% extends 'index.html' %}
    这时候我们访问网址:http://127.0.0.1:8000/accounts/login/?next=/ 结果如下:

          

    确认无误时,我们开始重写block,如下:
     1             {% extends 'index.html' %}
     2 
     3             {% block page-container %}
     4             <form action="" method="post"> {% csrf_token %}
     5                 Username:<input type="text" name="username">
     6                 Password:<input type="password" name="password">
     7                 <input type="submit" value="Log me in">
     8             </form>
     9 
    10             {% endblock %}
    访问测试如下:

    这时候我们随便输入用户名和密码就可以提交后台了.
    接下来我们就需要在views里对login.html中post的提交内容进行处理了
    1 def acc_login(request):
    2     if request.method == 'POST':
    3         print(request.POST)
    4     return render(request,'login.html')
    这时候我们在浏览器输入用户名和密码,在Django的命令行窗口输出如下内容:
    <QueryDict: {'username': ['admin'], 'password': ['admin123'], 'csrfmiddlewaretoken': ['p0P8hrImljJ7HVJYIYLVgpGniT70MicK']}>
    我们看到用户名和密码都被打印出来了.前面我们说过一般网站验证都不会名文验证,都是加盐加密.也就是收到用户输入的内容后,传到后台,后台在进行加盐加密封装后与数据库里的记录做对比.
    前面说过我们此节用户验证的部分要使用Django自带的而不是自己写.所以我们想把用户输入进来的账户进行验证,就要用到Django自带的验证方法..
    于是代码如下
     1 from django.shortcuts import render,redirect
     2 from django.contrib.auth.decorators import login_required
     3 from django.contrib.auth import authenticate
     4 def acc_login(request):
     5     if request.method == 'POST':
     6         print(request.POST)
     7         # 取出用户名和密码,实例化Django自己的用户验证实例
     8         user = authenticate(username = request.POST.get('username'),
     9                             password = request.POST.get('password')
    10                             )
    11         if user is not None:  # 如果用户不为空,则认证成功
    12             # return render()  # 当登录成功后,就不不是render一个页面了,应该跳转到首页了.
    13             return redirect('/') # 验证成功后,跳转到首页url
    14         else:
    15             # 如果登录失败,返回报错信息
    16             login_err = 'Wrong username or password'
    17             return render(request,'login.html',{'login_err':login_err})
    18     return render(request,'login.html')
    定义login.html
     1 {% extends 'index.html' %}
     2 
     3 {% block page-container %}
     4 <form action="" method="post">{% csrf_token %}
     5     Username:<input type="text" name="username">
     6     Password:<input type="password" name="password">
     7     <input type="submit" value="Log me in">
     8 </form>
     9 <div>
    10     {% if login_err %}
    11         <p style="color: red"> {{login_err}}</p>
    12     {% endif %}
    13 </div>
    14 {% endblock %}
    这时候我们访问首页http://127.0.0.1:8000/,首先会跳转到登录页面http://127.0.0.1:8000/accounts/login/?next=/
    输入错误的密码,提示错误,如下:

    当我们输入正确的用户名和密码,结果没有跳转(不是没跳转,只是又再次跳转到验证页面).为什么?
    因为我们只是在login中验证了账户的用户名和密码.而没有把session存下来.所以我们应该把session存下来
     1 from django.shortcuts import render,redirect,HttpResponseRedirect
     2 from django.contrib.auth.decorators import login_required
     3 from django.contrib.auth import authenticate,login #新导入login
     4 # 引入Django自带的用户验证模块
     5 # Create your views here.
     6 @login_required
     7 def index(request):
     8     return render(request,'index.html')
     9 
    10 def acc_login(request):
    11     if request.method == 'POST':
    12         print(request.POST)
    13         # 取出用户名和密码,实例化Django自己的用户验证实例
    14         user = authenticate(username = request.POST.get('username'),
    15                             password = request.POST.get('password')
    16                             )
    17         if user is not None:
    18             login(request,user)  # 实例化login,此时就会生成session,并保存到request
    19             return redirect('/') #
    20             # return HttpResponseRedirect('/') #也可以用这个跳转
    21         else:
    22             # 如果登录失败,返回报错信息
    23             login_err = 'Wrong username or password'
    24             return render(request,'login.html',{'login_err':login_err})
    25     return render(request,'login.html')
    我们再次访问测试:

      -》  


    跳转成功了,问题又来了,我如果想在首页中显示我的用户名,就像京东,右上角那样,未登录显示,"登录",登录后显示"用户名"
    我们首先想到的就是在模版html判断是不是登录,代码如下:
     1 <h1>oldboy CRM</h1>
     2 {% block page-container%}
     3     Welcome To Oldboy CRM
     4     <div>
     5         {% if request.user.is_authenticated %}
     6         <!--这里要注意,request在views中会自动返回,无论你在views视图里return时有没有返回-->
     7         <!--<span>{{ request.user }}</span>-->
     8         <!--这里显示的是用户名,想显示全名如下-->
     9         <span>{{ request.user.userprofile.name }}</span>
    10         {% else %}
    11         <span>登录/注册</span>
    12         {% endif %}
    13     </div>
    14 {% endblock %}
    15 </body>
    这时候我们就可以在页面看到用户全名了.
    接下来我们来看如何退出
    现在index.html写一个退出的按钮
     1 <body>
     2 <h1>oldboy CRM</h1>
     3 {% block page-container%}
     4     Welcome To Oldboy CRM
     5     <div>
     6         {% if request.user.is_authenticated %}
     7         <!--这里要注意,request在views中会自动返回,无论你在views视图里return时有没有返回-->
     8         <!--<span>{{ request.user }}</span>-->
     9         <!--这里显示的是用户名,想显示全名如下-->
    10         <span>{{ request.user.userprofile.name }}</span>
    11         {% else %}
    12         <span>登录/注册</span>
    13         {% endif %}
    14     </div>
    15 <a href="/accounts/logout/">退出系统</a>
    16 {% endblock %}
    17 </body>
    添加urls
    1 urlpatterns = [
    2     url(r'^admin/', admin.site.urls),
    3     url(r'^$', views.index),
    4     url(r'^accounts/login/$', views.acc_login),
    5     url(r'^accounts/logout/$', views.acc_logout),
    6 ]
    在app01/views.py添加退出视图
    1 from django.contrib.auth import authenticate,login,logout # 导入logout
    2 def acc_logout(request):
    3     logout(request)
    4     return HttpResponseRedirect('/')
    页面测试结果:

    -》



  • 相关阅读:
    QtCreator无法编辑源文件
    【Newtonsoft.Json】自己实现JsonConverter ,精简返回的数据结果
    Ghostscript 将PDF文件转换成PNG图片 问题一二
    Nginx--面试基础必会
    Nginx日志配置
    Nginx缓存原理及机制
    Nginx限流
    Nginx 实现 Rewrite 跳转
    Nginx正确配置Location
    渐进深入理解Nginx
  • 原文地址:https://www.cnblogs.com/zhming26/p/5796186.html
Copyright © 2011-2022 走看看