• Django+xadmin打造在线教育平台(二)


    目录

    在线教育平台(一)      在线教育平台(二)

    在线教育平台(三)      在线教育平台(四)

    在线教育平台(五)      在线教育平台(六)

    在线教育平台(七)      在线教育平台(八)

    在线教育平台(九)      在线教育平台(十)

    代码

    github下载

    在线演示

    教程

    学习自慕课网-使用python3.x与Django2.0.1开发的在线教育平台

    三、xadmin后台管理

    3.1.xadmin的安装

    django2.0的安装(源码安装方式):

    https://github.com/sshwsfc/xadmin/tree/django2

    把zip文件放到pip目录下,运行下面命令安装:

    pip install xadmin-django2
    是文件README.rst 出现了 Unicode 解码错误,这个文件是没有什么用处的,可以新建一个同名的空白文件替换掉
    
     首先下载zip源码包:github.com/sshwsfc/xadmin
    
     解压后,打开README.rst文件,清空里面的内容,然后保存。
    
    再压缩成zip,放到pip目录下:C:UsersAdministratorAppDataLocalProgramsPythonPython36Libsite-packagespip
    
    此时打开cmd进行安装:pip install xadmin-master.zip
    其它版本

    如果上面安装提示Runtime错误:

    更换安装源(使用豆瓣源)

    pip install -i https://pypi.douban.com/simple xadmin-django2

    安装成功后,同时也安装了很多依赖的包。

    3.2.xadmin的设置

     (1)新建Python Package "extra_apps",把源码xadmin文件夹放到extra_apps文件夹下面,此时目录结构如下:

    (2)把extra_apps右键mark为Source Root并在settings中加入

    sys.path.insert(0,os.path.join(BASE_DIR, 'extra_apps'))

    (3)因为我们用源码的xadmin,所以要卸载之前安装的

    pip uninstall xadmin

    (4)配置路由

    把admin改成xadmin

    # urls.py
    
    from django.urls import path
    
    import xadmin
    
    urlpatterns = [
        path('xadmin/', xadmin.site.urls),
    ]

    (5)注册app

    把下面两个app注册到settings.py的INSTALLED_APPS中

    'xadmin',
    'crispy_forms'

    (6)重新生成数据库

    python manage.py makemigrations
    
    python manage.py migrate

    (7)设置成中文

    LANGUAGE_CODE = 'zh-hans'
    
    TIME_ZONE = 'Asia/Shanghai'
    
    USE_I18N = True
    
    USE_L10N = True
    
    USE_TZ = False

    (8)创建一个管理员用户

    python manage.py createsuperuser

    现在就可以运行了 

    python manage.py runserver 

    访问后台:http://127.0.0.1:8000/xadmin

     可以看到成功进入管理界面

    datetimefield报错问题解决:

    当我们点增加用户信息,会报错

    可以看到报的是xadmin/widgets中第80行

     def render(self, name, value, attrs=None):
            input_html = [ht for ht in super(AdminSplitDateTime, self).render(name, value, attrs).split('
    ') if ht != '']
            # return input_html
            return mark_safe('<div class="datetime clearfix"><div class="input-group date bootstrap-datepicker"><span class="input-group-addon"><i class="fa fa-calendar"></i></span>%s'
                             '<span class="input-group-btn"><button class="btn btn-default" type="button">%s</button></span></div>'
                             '<div class="input-group time bootstrap-clockpicker"><span class="input-group-addon"><i class="fa fa-clock-o">'
                             '</i></span>%s<span class="input-group-btn"><button class="btn btn-default" type="button">%s</button></span></div></div>' % (input_html[0], _(u'Today'), input_html[1], _(u'Now')))

    上面贴出来的最后一行代码就是widgets.py的第80行代码。

    可以看出这句代码是希望用“ ”把input_html里的两个标签拆开,但两个标签之间没有换行,所以没能拆分,导致报错。

    input_html[1]就是报错的代码,因为input_html里只有一个元素。

     解决办法:

    既然“ ”不能拆分标签,那么就换一种拆分方式,使用“/><”拆分。

    原代码:

    input_html = [ht for ht in super(AdminSplitDateTime, self).render(name, value, attrs).split('
    ') if ht != '']

    修改后代码:

    input_html = [ht for ht in super(AdminSplitDateTime, self).render(name, value, attrs).split('/><') if ht != '']
    input_html[0] = input_html[0] + "/>"
    input_html[1] = "<" + input_html[1]

    再运行就正常了

    3.3.users app的models注册

    (1)在users下面创建adminx.py,代码如下:

    # users/adminx.py
    
    import xadmin
    
    from .models import EmailVerifyRecord
    
    #xadmin中这里是继承object,不再是继承admin
    class EmailVerifyRecordAdmin(object):
        pass
    
    xadmin.site.register(EmailVerifyRecord,EmailVerifyRecordAdmin)

    (2)完善功能,增加显示字段,搜索和过滤

    修改users/adminx.py,代码如下:

    # users/adminx.py
    
    import xadmin
    
    from .models import EmailVerifyRecord
    
    #xadmin中这里是继承object,不再是继承admin
    class EmailVerifyRecordAdmin(object):
        # 显示的列
        list_display = ['code', 'email', 'send_type', 'send_time']
        # 搜索的字段,不要添加时间搜索
        search_fields = ['code', 'email', 'send_type']
        # 过滤
        list_filter = ['code', 'email', 'send_type', 'send_time']
    
    xadmin.site.register(EmailVerifyRecord,EmailVerifyRecordAdmin)

    刷新后的界面:

    users中Banner也注册进去

    class BannerAdmin(object):
        list_display = ['title', 'image', 'url','index', 'add_time']
        search_fields = ['title', 'image', 'url','index']
        list_filter = ['title', 'image', 'url','index', 'add_time']
    
    
    xadmin.site.register(Banner,BannerAdmin)

    3.4.剩余app model注册

    (1)course

    代码如下: 注意外键

    # course/adminx.py
    
    import xadmin
    
    from .models import Course, Lesson, Video, CourseResource
    
    class CourseAdmin(object): '''课程''' list_display = [ 'name','desc','detail','degree','learn_times','students'] search_fields = ['name', 'desc', 'detail', 'degree', 'students'] list_filter = [ 'name','desc','detail','degree','learn_times','students'] class LessonAdmin(object): '''章节''' list_display = ['course', 'name', 'add_time'] search_fields = ['course', 'name'] #这里course__name是根据课程名称过滤 list_filter = ['course__name', 'name', 'add_time'] class VideoAdmin(object): '''视频''' list_display = ['lesson', 'name', 'add_time'] search_fields = ['lesson', 'name'] list_filter = ['lesson', 'name', 'add_time'] class CourseResourceAdmin(object): '''课程资源''' list_display = ['course', 'name', 'download', 'add_time'] search_fields = ['course', 'name', 'download'] list_filter = ['course__name', 'name', 'download', 'add_time'] # 将管理器与model进行注册关联 xadmin.site.register(Course, CourseAdmin) xadmin.site.register(Lesson, LessonAdmin) xadmin.site.register(Video, VideoAdmin) xadmin.site.register(CourseResource, CourseResourceAdmin)

    (2)organizations

     代码如下:

    # organization/adminx.py
    
    import xadmin
    
    from .models import CityDict, CourseOrg, Teacher
    
    
    
    class CityDictAdmin(object):
        '''城市'''
        
        list_display = ['name', 'desc', 'add_time']
        search_fields = ['name', 'desc']
        list_filter = ['name', 'desc', 'add_time']
    
    
    class CourseOrgAdmin(object):
        '''机构'''
        
        list_display = ['name', 'desc', 'click_nums', 'fav_nums','add_time' ]
        search_fields = ['name', 'desc', 'click_nums', 'fav_nums']
        list_filter = ['name', 'desc', 'click_nums', 'fav_nums','city__name','address','add_time']
    
    
    class TeacherAdmin(object):
        '''老师'''
        
        list_display = [ 'name','org', 'work_years', 'work_company','add_time']
        search_fields = ['org', 'name', 'work_years', 'work_company']
        list_filter = ['org__name', 'name', 'work_years', 'work_company','click_nums', 'fav_nums', 'add_time']
    
    
    xadmin.site.register(CityDict, CityDictAdmin)
    xadmin.site.register(CourseOrg, CourseOrgAdmin)
    xadmin.site.register(Teacher, TeacherAdmin)

    (3)operation

    代码如下:

    # operation/adminx.py
    
    import xadmin
    
    from .models import UserAsk, UserCourse, UserMessage, CourseComments, UserFavorite
    
    
    class UserAskAdmin(object):
        '''用户表单我要学习'''
    
        list_display = ['name', 'mobile', 'course_name', 'add_time']
        search_fields = ['name', 'mobile', 'course_name']
        list_filter = ['name', 'mobile', 'course_name', 'add_time']
    
    
    #
    class UserCourseAdmin(object):
        '''用户课程学习'''
    
        list_display = ['user', 'course', 'add_time']
        search_fields = ['user', 'course']
        list_filter = ['user', 'course', 'add_time']
    
    
    
    class UserMessageAdmin(object):
        '''用户消息后台'''
    
        list_display = ['user', 'message', 'has_read', 'add_time']
        search_fields = ['user', 'message', 'has_read']
        list_filter = ['user', 'message', 'has_read', 'add_time']
    
    
    
    class CourseCommentsAdmin(object):
        '''用户评论后台'''
    
        list_display = ['user', 'course', 'comments', 'add_time']
        search_fields = ['user', 'course', 'comments']
        list_filter = ['user', 'course', 'comments', 'add_time']
    
    
    
    class UserFavoriteAdmin(object):
        '''用户收藏后台'''
    
        list_display = ['user', 'fav_id', 'fav_type', 'add_time']
        search_fields = ['user', 'fav_id', 'fav_type']
        list_filter = ['user', 'fav_id', 'fav_type', 'add_time']
    
    
    # 将后台管理器与models进行关联注册。
    xadmin.site.register(UserAsk, UserAskAdmin)
    xadmin.site.register(UserCourse, UserCourseAdmin)
    xadmin.site.register(UserMessage, UserMessageAdmin)
    xadmin.site.register(CourseComments, CourseCommentsAdmin)
    xadmin.site.register(UserFavorite, UserFavoriteAdmin)

     全部代码:

    # users/adminx.py
    
    import xadmin
    
    from .models import EmailVerifyRecord,Banner
    
    #xadmin中这里是继承object,不再是继承admin
    class EmailVerifyRecordAdmin(object):
        # 显示的列
        list_display = ['code', 'email', 'send_type', 'send_time']
        # 搜索的字段
        search_fields = ['code', 'email', 'send_type']
        # 过滤
        list_filter = ['code', 'email', 'send_type', 'send_time']
    
    
    class BannerAdmin(object):
        list_display = ['title', 'image', 'url','index', 'add_time']
        search_fields = ['title', 'image', 'url','index']
        list_filter = ['title', 'image', 'url','index', 'add_time']
    
    
    xadmin.site.register(EmailVerifyRecord,EmailVerifyRecordAdmin)
    xadmin.site.register(Banner,BannerAdmin)
    users/adminx.py
    # course/adminx.py
    
    import xadmin
    
    from .models import Course, Lesson, Video, CourseResource
    
    
    # Course的admin管理器
    class CourseAdmin(object):
        '''课程'''
    
        list_display = [ 'name','desc','detail','degree','learn_times','students']
        search_fields = ['name', 'desc', 'detail', 'degree', 'students']
        list_filter = [ 'name','desc','detail','degree','learn_times','students']
    
    
    class LessonAdmin(object):
        '''章节'''
    
        list_display = ['course', 'name', 'add_time']
        search_fields = ['course', 'name']
        #这里course__name是根据课程名称过滤
        list_filter = ['course__name', 'name', 'add_time']
    
    
    class VideoAdmin(object):
        '''视频'''
    
        list_display = ['lesson', 'name', 'add_time']
        search_fields = ['lesson', 'name']
        list_filter = ['lesson', 'name', 'add_time']
    
    
    class CourseResourceAdmin(object):
        '''课程资源'''
    
        list_display = ['course', 'name', 'download', 'add_time']
        search_fields = ['course', 'name', 'download']
        list_filter = ['course__name', 'name', 'download', 'add_time']
    
    
    # 将管理器与model进行注册关联
    xadmin.site.register(Course, CourseAdmin)
    xadmin.site.register(Lesson, LessonAdmin)
    xadmin.site.register(Video, VideoAdmin)
    xadmin.site.register(CourseResource, CourseResourceAdmin)
    course/adminx.py
    # organization/adminx.py
    
    import xadmin
    
    from .models import CityDict, CourseOrg, Teacher
    
    
    class CityDictAdmin(object):
        '''城市'''
    
        list_display = ['name', 'desc', 'add_time']
        search_fields = ['name', 'desc']
        list_filter = ['name', 'desc', 'add_time']
    
    
    class CourseOrgAdmin(object):
        '''机构'''
    
        list_display = ['name', 'desc', 'click_nums', 'fav_nums','add_time' ]
        search_fields = ['name', 'desc', 'click_nums', 'fav_nums']
        list_filter = ['name', 'desc', 'click_nums', 'fav_nums','city__name','address','add_time']
    
    
    class TeacherAdmin(object):
        '''老师'''
    
        list_display = [ 'name','org', 'work_years', 'work_company','add_time']
        search_fields = ['org', 'name', 'work_years', 'work_company']
        list_filter = ['org__name', 'name', 'work_years', 'work_company','click_nums', 'fav_nums', 'add_time']
    
    
    xadmin.site.register(CityDict, CityDictAdmin)
    xadmin.site.register(CourseOrg, CourseOrgAdmin)
    xadmin.site.register(Teacher, TeacherAdmin)
    organization/adminx.py
    # operation/adminx.py
    
    import xadmin
    
    from .models import UserAsk, UserCourse, UserMessage, CourseComments, UserFavorite
    
    
    class UserAskAdmin(object):
        '''用户表单我要学习'''
    
        list_display = ['name', 'mobile', 'course_name', 'add_time']
        search_fields = ['name', 'mobile', 'course_name']
        list_filter = ['name', 'mobile', 'course_name', 'add_time']
    
    
    #
    class UserCourseAdmin(object):
        '''用户课程学习'''
    
        list_display = ['user', 'course', 'add_time']
        search_fields = ['user', 'course']
        list_filter = ['user', 'course', 'add_time']
    
    
    
    class UserMessageAdmin(object):
        '''用户消息后台'''
    
        list_display = ['user', 'message', 'has_read', 'add_time']
        search_fields = ['user', 'message', 'has_read']
        list_filter = ['user', 'message', 'has_read', 'add_time']
    
    
    
    class CourseCommentsAdmin(object):
        '''用户评论后台'''
    
        list_display = ['user', 'course', 'comments', 'add_time']
        search_fields = ['user', 'course', 'comments']
        list_filter = ['user', 'course', 'comments', 'add_time']
    
    
    
    class UserFavoriteAdmin(object):
        '''用户收藏后台'''
    
        list_display = ['user', 'fav_id', 'fav_type', 'add_time']
        search_fields = ['user', 'fav_id', 'fav_type']
        list_filter = ['user', 'fav_id', 'fav_type', 'add_time']
    
    
    # 将后台管理器与models进行关联注册。
    xadmin.site.register(UserAsk, UserAskAdmin)
    xadmin.site.register(UserCourse, UserCourseAdmin)
    xadmin.site.register(UserMessage, UserMessageAdmin)
    xadmin.site.register(CourseComments, CourseCommentsAdmin)
    xadmin.site.register(UserFavorite, UserFavoriteAdmin)
    operation/adminx.py

    此时项目目录结构:

     运行项目,进后台管理界面如下:

     3.5.xadmin的全局配置

    将全局配置修改:

    • 如左上角:django Xadmin。下面的我的公司
    • 主题修改,app名称汉化,菜单收叠。

     使用Xadmin的主题功能。

    把全站的配置放在usersadminx.py中:

     (1)添加主题功能

    from xadmin import views
    
    # 创建xadmin的最基本管理器配置,并与view绑定
    class BaseSetting(object):
        # 开启主题功能
        enable_themes = True
        use_bootswatch = True
    
    # 将基本配置管理与view绑定
    xadmin.site.register(views.BaseAdminView,BaseSetting)

    没添加主题前,右上角界面

    添加主题后,可以选择自己喜欢的主题

    (2)全局配置

     修改django admin 和下面的我的公司收起菜单

    # 全局修改,固定写法
    class GlobalSettings(object):
        # 修改title
        site_title = 'NBA后台管理界面'
        # 修改footer
        site_footer = '科比的公司'
        # 收起菜单
        menu_style = 'accordion'
    
    # 将title和footer信息进行注册
    xadmin.site.register(views.CommAdminView,GlobalSettings)
    # users/adminx.py
    
    import xadmin
    
    from .models import EmailVerifyRecord,Banner
    
    from xadmin import views
    
    # 创建xadmin的最基本管理器配置,并与view绑定
    class BaseSetting(object):
        # 开启主题功能
        enable_themes = True
        use_bootswatch = True
    
    # 全局修改,固定写法
    class GlobalSettings(object):
        # 修改title
        site_title = 'NBA后台管理界面'
        # 修改footer
        site_footer = '科比的公司'
        # 收起菜单
        menu_style = 'accordion'
    
    
    #xadmin中这里是继承object,不再是继承admin
    class EmailVerifyRecordAdmin(object):
        # 显示的列
        list_display = ['code', 'email', 'send_type', 'send_time']
        # 搜索的字段
        search_fields = ['code', 'email', 'send_type']
        # 过滤
        list_filter = ['code', 'email', 'send_type', 'send_time']
    
    
    class BannerAdmin(object):
        list_display = ['title', 'image', 'url','index', 'add_time']
        search_fields = ['title', 'image', 'url','index']
        list_filter = ['title', 'image', 'url','index', 'add_time']
    
    
    xadmin.site.register(EmailVerifyRecord,EmailVerifyRecordAdmin)
    xadmin.site.register(Banner,BannerAdmin)
    
    # 将基本配置管理与view绑定
    xadmin.site.register(views.BaseAdminView,BaseSetting)
    
    # 将title和footer信息进行注册
    xadmin.site.register(views.CommAdminView,GlobalSettings)
    users/adminx.py全部代码

    再进后台的界面,如下:

    (3)修改app的名字

     在apps.py里面配置app的显示名称

     以users/apps.py为例,其它三个同样操作

    默认apps.py里面的代码

    from django.apps import AppConfig
    
    
    class UsersConfig(AppConfig):
        name = 'users'

    修改后:

    from django.apps import AppConfig
    
    
    class UsersConfig(AppConfig):
        name = 'users'
        verbose_name = '用户'

    还要在users/__init__.py中引用apps.py的配置

    添加代码如下:

    # users/__init__.py
    
    default_app_config = 'users.apps.UsersConfig'

    其它三个app也同样方法改成显示中文

     大功告成

    四、完成登录功能

    4.1.首页和登录页面的配置

    (1)把html文件中index.html拷贝到templates文件夹内

    前端初始文件可以去我github上面下载:https://github.com/derek-zhang123/MxOnline

    (2)新建static目录用来存放静态文件

    在settings.py中设置路径

    STATICFILES_DIRS = (
        os.path.join(BASE_DIR,'static'),
    )

    (3)引用静态文件

     使用ctrl+f查找出所有“../”, 然后ctrl+r 全部替换为“/static/”

    (4)配置静态文件的url

    MxOnline/urls.py中

    # MxOnline/urls.py
    
    import xadmin
    
    from django.urls import path
    
    from django.views.generic import TemplateView
    
    urlpatterns = [
        path('xadmin/', xadmin.site.urls),
        path('', TemplateView.as_view(template_name='index.html'),name='index'),
    ]

    (5)登录页面

    把login.html拷贝到templates文件夹下

    使用ctrl+f查找出所有“../”, 然后ctrl+r 全部替换为“/static/”

     配置login的url

    # MxOnline/urls.py
    
    urlpatterns = [
        path('xadmin/', xadmin.site.urls),
        path('', TemplateView.as_view(template_name='index.html'),name='index'),
        path('login/', TemplateView.as_view(template_name='login.html'),name='login'),
    ]

    更改index.html里面跳转到登录界面的url

    原始样子

    <!-- <a style="color:white" class="fr registerbtn" href="register.html">注册</a> -->

    <!-- <a style="color:white" class="fr loginbtn" href="login.html">登录</a> -->

    取消注释,将login.html改为“login/”

    <a style="color:white" class="fr registerbtn" href="register.html">注册</a>
    <a style="color:white" class="fr loginbtn" href="/login/">登录</a>

    现在可以访问index页面,然后点‘’登录”,跳转到登录页面了

    4.2.用户登录

    (1)修改login的路由

    from django.views.generic import TemplateView
    from users import views
    
    
    urlpatterns = [
        path('xadmin/', xadmin.site.urls),
        path('', TemplateView.as_view(template_name='index.html'),name='index'),
        path('login/',views.user_login,name = 'login'),     #修改login路由
    ]

    (2)写login的视图

    from django.shortcuts import render
    from django.contrib.auth import authenticate,login
    
    def user_login(request):
        if request.method == 'POST':
            # 获取用户提交的用户名和密码
            user_name = request.POST.get('username',None)
            pass_word = request.POST.get('password',None)
            # 成功返回user对象,失败None
            user = authenticate(username=user_name,password=pass_word)
            # 如果不是null说明验证成功
            if user is not None:
                # 登录
                login(request,user)
                return render(request,'index.html')
            else:
                return render(request,'login.html',{'msg':'用户名或密码错误'})
        
        elif request.method == 'GET':
            return render(request,'login.html')

    (3)更改login.html

     <form action="/login/" method="post" autocomplete="off">
                        <input type='hidden' name='csrfmiddlewaretoken' value='mymQDzHWl2REXIfPMg2mJaLqDfaS1sD5' />
                        <div class="form-group marb20 ">
                            <label>&nbsp;&nbsp;</label>
                            <input name="username" id="account_l" type="text" placeholder="手机号/邮箱" />
                        </div>
                        <div class="form-group marb8 ">
                            <label>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
                            <input name="password" id="password_l" type="password" placeholder="请输入您的密码" />
                        </div>
                        <div class="error btns login-form-tips" id="jsLoginTips">{{ msg }}</div>
                         <div class="auto-box marb38">
    
                            <a class="fr" href="forgetpwd.html">忘记密码?</a>
                         </div>
                         <input class="btn btn-green" id="jsLoginBtn" type="submit" value="立即登录 > " />
                    <input type='hidden' name='csrfmiddlewaretoken' value='5I2SlleZJOMUX9QbwYLUIAOshdrdpRcy' />
                        {% csrf_token %}
                    </form>
    login.html

    如果用户登录错误,应该有提示错误信息,下面代码:

    <div class="error btns login-form-tips" id="jsLoginTips">{{ msg }}</div>

    (4)修改index.html

    原始index.html的代码

    <div  class=" header">
                 <div class="top">
                    <div class="wp">
                        <div class="fl"><p>服务电话:<b>33333333</b></p></div>
                        <!--登录后跳转-->
    
                            
                             <a style="color:white" class="fr registerbtn" href="register.html">注册</a>
                             <a style="color:white" class="fr loginbtn" href="/login/">登录</a>
                            
                            <div class="personal">
                                <dl class="user fr">
                                    <dd>bobby<img class="down fr" src="/static/images/top_down.png"/></dd>
                                    <dt><img width="20" height="20" src="/static/media/image/2016/12/default_big_14.png"/></dt>
                                </dl>
                                <div class="userdetail">
                                    <dl>
                                        <dt><img width="80" height="80" src="/static/media/image/2016/12/default_big_14.png"/></dt>
                                        <dd>
                                            <h2>django</h2>
                                            <p>bobby</p>
                                        </dd>
                                    </dl>
                                    <div class="btn">
                                        <a class="personcenter fl" href="usercenter-info.html">进入个人中心</a>
                                        <a class="fr" href="/logout/">退出</a>
                                    </div>
                                </div>
                            </div>
    
    
                    </div>
                </div>

     我们应该做个验证,当用户已登录状态的时候,显示用户姓名和图像及其个人中心信息

     如果没有登录,则显示登录和注册

    更改代码如下:

    <div  class=" header">
                 <div class="top">
                    {% if request.user.is_authenticated %}
                    <div class="personal">
                                <dl class="user fr">
                                    <dd>bobby<img class="down fr" src="/static/images/top_down.png"/></dd>
                                    <dt><img width="20" height="20" src="/static/media/image/2016/12/default_big_14.png"/></dt>
                                </dl>
                                <div class="userdetail">
                                    <dl>
                                        <dt><img width="80" height="80" src="/static/media/image/2016/12/default_big_14.png"/></dt>
                                        <dd>
                                            <h2>django</h2>
                                            <p>bobby</p>
                                        </dd>
                                    </dl>
                                    <div class="btn">
                                        <a class="personcenter fl" href="usercenter-info.html">进入个人中心</a>
                                        <a class="fr" href="/logout/">退出</a>
                                    </div>
                                </div>
                            </div>
                    {% else %}
                    <div class="wp">
                        <div class="fl"><p>服务电话:<b>33333333</b></p></div>
                        <!--登录后跳转-->
    
                            
                             <a style="color:white" class="fr registerbtn" href="register.html">注册</a>
                             <a style="color:white" class="fr loginbtn" href="/login/">登录</a>
                            
    
    
    
                    </div>
                    {% endif %}
                </div>
    index.html

     (5)增加邮箱登录

     让用户可以通过邮箱或者用户名都可以登录,用自定义authenticate方法

    这里是继承ModelBackend类来做的验证

    class ModelBackend:
        """
        Authenticates against settings.AUTH_USER_MODEL.
        """
    
        def authenticate(self, request, username=None, password=None, **kwargs):
            if username is None:
                username = kwargs.get(UserModel.USERNAME_FIELD)
            try:
                user = UserModel._default_manager.get_by_natural_key(username)
            except UserModel.DoesNotExist:
                # Run the default password hasher once to reduce the timing
                # difference between an existing and a nonexistent user (#20760).
                UserModel().set_password(password)
            else:
                if user.check_password(password) and self.user_can_authenticate(user):
                    return user
    
        def user_can_authenticate(self, user):
            """
            Reject users with is_active=False. Custom user models that don't have
            that attribute are allowed.
            """
            is_active = getattr(user, 'is_active', None)
            return is_active or is_active is None
    
        def _get_user_permissions(self, user_obj):
            return user_obj.user_permissions.all()
    
        def _get_group_permissions(self, user_obj):
            user_groups_field = get_user_model()._meta.get_field('groups')
            user_groups_query = 'group__%s' % user_groups_field.related_query_name()
            return Permission.objects.filter(**{user_groups_query: user_obj})
    
        def _get_permissions(self, user_obj, obj, from_name):
            """
            Return the permissions of `user_obj` from `from_name`. `from_name` can
            be either "group" or "user" to return permissions from
            `_get_group_permissions` or `_get_user_permissions` respectively.
            """
            if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
                return set()
    
            perm_cache_name = '_%s_perm_cache' % from_name
            if not hasattr(user_obj, perm_cache_name):
                if user_obj.is_superuser:
                    perms = Permission.objects.all()
                else:
                    perms = getattr(self, '_get_%s_permissions' % from_name)(user_obj)
                perms = perms.values_list('content_type__app_label', 'codename').order_by()
                setattr(user_obj, perm_cache_name, {"%s.%s" % (ct, name) for ct, name in perms})
            return getattr(user_obj, perm_cache_name)
    
        def get_user_permissions(self, user_obj, obj=None):
            """
            Return a set of permission strings the user `user_obj` has from their
            `user_permissions`.
            """
            return self._get_permissions(user_obj, obj, 'user')
    
        def get_group_permissions(self, user_obj, obj=None):
            """
            Return a set of permission strings the user `user_obj` has from the
            groups they belong.
            """
            return self._get_permissions(user_obj, obj, 'group')
    
        def get_all_permissions(self, user_obj, obj=None):
            if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
                return set()
            if not hasattr(user_obj, '_perm_cache'):
                user_obj._perm_cache = set()
                user_obj._perm_cache.update(self.get_user_permissions(user_obj))
                user_obj._perm_cache.update(self.get_group_permissions(user_obj))
            return user_obj._perm_cache
    
        def has_perm(self, user_obj, perm, obj=None):
            if not user_obj.is_active:
                return False
            return perm in self.get_all_permissions(user_obj, obj)
    
        def has_module_perms(self, user_obj, app_label):
            """
            Return True if user_obj has any permissions in the given app_label.
            """
            if not user_obj.is_active:
                return False
            for perm in self.get_all_permissions(user_obj):
                if perm[:perm.index('.')] == app_label:
                    return True
            return False
    
        def get_user(self, user_id):
            try:
                user = UserModel._default_manager.get(pk=user_id)
            except UserModel.DoesNotExist:
                return None
            return user if self.user_can_authenticate(user) else None
    ModelBackend源码
    from django.contrib.auth.backends import ModelBackend
    from .models import UserProfile
    from django.db.models import Q
    
    #邮箱和用户名都可以登录
    # 基础ModelBackend类,因为它有authenticate方法
    class CustomBackend(ModelBackend):
        def authenticate(self, request, username=None, password=None, **kwargs):
            try:
                # 不希望用户存在两个,get只能有一个。两个是get失败的一种原因 Q为使用并集查询
                user = UserProfile.objects.get(Q(username=username)|Q(email=username))
    
                # django的后台中密码加密:所以不能password==password
                # UserProfile继承的AbstractUser中有def check_password(self, raw_password):
                if user.check_password(password):
                    return user
            except Exception as e:
                return None
    # users/views.py
    
    from django.shortcuts import render
    from django.contrib.auth import authenticate,login
    
    from django.contrib.auth.backends import ModelBackend
    from .models import UserProfile
    from django.db.models import Q
    
    #邮箱和用户名都可以登录
    # 基础ModelBackend类,因为它有authenticate方法
    class CustomBackend(ModelBackend):
        def authenticate(self, request, username=None, password=None, **kwargs):
            try:
                # 不希望用户存在两个,get只能有一个。两个是get失败的一种原因 Q为使用并集查询
                user = UserProfile.objects.get(Q(username=username)|Q(email=username))
    
                # django的后台中密码加密:所以不能password==password
                # UserProfile继承的AbstractUser中有def check_password(self, raw_password):
                if user.check_password(password):
                    return user
            except Exception as e:
                return None
    
    def user_login(request):
        if request.method == 'POST':
            # 获取用户提交的用户名和密码
            user_name = request.POST.get('username',None)
            pass_word = request.POST.get('password',None)
            # 成功返回user对象,失败None
            user = authenticate(username=user_name,password=pass_word)
            # 如果不是null说明验证成功
            if user is not None:
                # 登录
                login(request,user)
                return render(request,'index.html')
            else:
                return render(request,'login.html',{'msg':'用户名或密码错误'})
    
        elif request.method == 'GET':
            return render(request,'login.html')
    users/views.py

    MxOnline/settings.py添加如下代码:

    AUTHENTICATION_BACKENDS = (
        'users.views.CustomBackend',
    )

    然后通过邮箱也可以实现登录了

    4.3.用form实现登录

    (1)把前面views中的user_login()函数改成基于类的形式

    from django.views.generic.base import View
    
    class LoginView(View):
        def get(self,request):
            return render(request, 'login.html')
    
        def post(self,request):
            # 获取用户提交的用户名和密码
            user_name = request.POST.get('username', None)
            pass_word = request.POST.get('password', None)
            # 成功返回user对象,失败None
            user = authenticate(username=user_name, password=pass_word)
            # 如果不是null说明验证成功
            if user is not None:
                # 登录
                login(request, user)
                return render(request, 'index.html')
            else:
                return render(request, 'login.html', {'msg': '用户名或密码错误'})

    继承的View类

    class View:
        """
        Intentionally simple parent class for all views. Only implements
        dispatch-by-method and simple sanity checking.
        """
    
        http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
    
        def __init__(self, **kwargs):
            """
            Constructor. Called in the URLconf; can contain helpful extra
            keyword arguments, and other things.
            """
            # Go through keyword arguments, and either save their values to our
            # instance, or raise an error.
            for key, value in kwargs.items():
                setattr(self, key, value)
    
        @classonlymethod
        def as_view(cls, **initkwargs):
            """Main entry point for a request-response process."""
            for key in initkwargs:
                if key in cls.http_method_names:
                    raise TypeError("You tried to pass in the %s method name as a "
                                    "keyword argument to %s(). Don't do that."
                                    % (key, cls.__name__))
                if not hasattr(cls, key):
                    raise TypeError("%s() received an invalid keyword %r. as_view "
                                    "only accepts arguments that are already "
                                    "attributes of the class." % (cls.__name__, key))
    
            def view(request, *args, **kwargs):
                self = cls(**initkwargs)
                if hasattr(self, 'get') and not hasattr(self, 'head'):
                    self.head = self.get
                self.request = request
                self.args = args
                self.kwargs = kwargs
                return self.dispatch(request, *args, **kwargs)
            view.view_class = cls
            view.view_initkwargs = initkwargs
    
            # take name and docstring from class
            update_wrapper(view, cls, updated=())
    
            # and possible attributes set by decorators
            # like csrf_exempt from dispatch
            update_wrapper(view, cls.dispatch, assigned=())
            return view
    
        def dispatch(self, request, *args, **kwargs):
            # Try to dispatch to the right method; if a method doesn't exist,
            # defer to the error handler. Also defer to the error handler if the
            # request method isn't on the approved list.
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            return handler(request, *args, **kwargs)
    
        def http_method_not_allowed(self, request, *args, **kwargs):
            logger.warning(
                'Method Not Allowed (%s): %s', request.method, request.path,
                extra={'status_code': 405, 'request': request}
            )
            return HttpResponseNotAllowed(self._allowed_methods())
    
        def options(self, request, *args, **kwargs):
            """Handle responding to requests for the OPTIONS HTTP verb."""
            response = HttpResponse()
            response['Allow'] = ', '.join(self._allowed_methods())
            response['Content-Length'] = '0'
            return response
    
        def _allowed_methods(self):
            return [m.upper() for m in self.http_method_names if hasattr(self, m)]
    View类源码参考

    基于类的urls配置

    from users.views import LoginView
    
      path('login/',LoginView.as_view(),name = 'login'),

    (2)users下新建form.py文件

     代码如下:

    # users/forms.py
    
    from django import forms
    
    # 登录表单验证
    class LoginForm(forms.Form):
        # 用户名密码不能为空
        username = forms.CharField(required=True)
        password = forms.CharField(required=True,min_length=5)

    (3)定义好forms后利用它来做验证,并完善错误提示信息

    from .forms import LoginForm
    
    class LoginView(View):
        def get(self,request):
            return render(request, 'login.html')
    
        def post(self,request):
            # 实例化
            login_form = LoginForm(request.POST)
            if login_form.is_valid():
                # 获取用户提交的用户名和密码
                user_name = request.POST.get('username', None)
                pass_word = request.POST.get('password', None)
                # 成功返回user对象,失败None
                user = authenticate(username=user_name, password=pass_word)
                # 如果不是null说明验证成功
                if user is not None:
                    # 登录
                    login(request, user)
                    return render(request, 'index.html')
                # 只有当用户名或密码不存在时,才返回错误信息到前端
                else:
                    return render(request, 'login.html', {'msg': '用户名或密码错误','login_form':login_form})
                
            # form.is_valid()已经判断不合法了,所以这里不需要再返回错误信息到前端了
            else:
                return render(request,'login.html',{'login_form':login_form})
    # users/views.py
    
    from django.shortcuts import render
    from django.contrib.auth import authenticate,login
    
    from django.contrib.auth.backends import ModelBackend
    from .models import UserProfile
    from django.db.models import Q
    from django.views.generic.base import View
    from .forms import LoginForm
    
    #邮箱和用户名都可以登录
    # 基础ModelBackend类,因为它有authenticate方法
    class CustomBackend(ModelBackend):
        def authenticate(self, request, username=None, password=None, **kwargs):
            try:
                # 不希望用户存在两个,get只能有一个。两个是get失败的一种原因 Q为使用并集查询
                user = UserProfile.objects.get(Q(username=username)|Q(email=username))
    
                # django的后台中密码加密:所以不能password==password
                # UserProfile继承的AbstractUser中有def check_password(self, raw_password):
                if user.check_password(password):
                    return user
            except Exception as e:
                return None
    
    
    class LoginView(View):
        def get(self,request):
            return render(request, 'login.html')
    
        def post(self,request):
            # 实例化
            login_form = LoginForm(request.POST)
            if login_form.is_valid():
                # 获取用户提交的用户名和密码
                user_name = request.POST.get('username', None)
                pass_word = request.POST.get('password', None)
                # 成功返回user对象,失败None
                user = authenticate(username=user_name, password=pass_word)
                # 如果不是null说明验证成功
                if user is not None:
                    # 登录
                    login(request, user)
                    return render(request, 'index.html')
                # 只有当用户名或密码不存在时,才返回错误信息到前端
                else:
                    return render(request, 'login.html', {'msg': '用户名或密码错误','login_form':login_form})
    
            # form.is_valid()已经判断不合法了,所以这里不需要再返回错误信息到前端了
            else:
                return render(request,'login.html',{'login_form':login_form})
    views.py

    (4)完善login.html的错误提示信息

    <div class="form-group marb20 {% if login_form.errors.username %}errorput{% endif %}">
                            <label>&nbsp;&nbsp;</label>
                            <input name="username" id="account_l" type="text" placeholder="手机号/邮箱" />
                        </div>
                        <div class="form-group marb8 {% if login_form.errors.username %}errorput{% endif %}">
                            <label>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
                            <input name="password" id="password_l" type="password" placeholder="请输入您的密码" />
                        </div>
                        <div class="error btns login-form-tips" id="jsLoginTips">
                            {% for key,error in login_form.errors.items %}
                                {{ error }}
                            {% endfor %}
                            {{ msg }}
                        </div>

    主要修改两处

    <!DOCTYPE html>
    <html>
    
    <head>
        <meta charset="UTF-8">
        <meta name="renderer" content="webkit">
        <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" >
        <title>慕学在线网登录</title>
        <link rel="stylesheet" type="text/css" href="/static/css/reset.css">
        <link rel="stylesheet" type="text/css" href="/static/css/login.css">
    </head>
    <body>
    <div class="dialog" id="jsDialog">
    <!--提示弹出框-->
    <div class="successbox dialogbox" id="jsSuccessTips">
        <h1>成功提交</h1>
        <div class="close jsCloseDialog"><img src="/static/images/dig_close.png"/></div>
        <div class="cont">
            <h2>您的需求提交成功!</h2>
            <p></p>
        </div>
    </div>
    <div  class="noactivebox dialogbox" id="jsUnactiveForm" >
        <h1>邮件验证提示</h1>
        <div class="close jsCloseDialog"><img src="/static/images/dig_close.png"/></div>
        <div class="center">
            <img src="/static/images/send.png"/>
            <p>我们已经向您的邮箱<span class="green" id="jsEmailToActive">12@13.com</span>发送了邮件,<br/>为保证您的账号安全,请及时验证邮箱</p>
            <p class="a"><a class="btn" id="jsGoToEmail" target="_blank" href="http://mail.qq.com">去邮箱验证</a></p>
            <p class="zy_success upmove"></p>
            <p style="display: none;" class="sendE2">没收到,您可以查看您的垃圾邮件和被过滤邮件,也可以再次发送验证邮件(<span class="c5c">60s</span></p>
            <p class="sendE">没收到,您可以查看您的垃圾邮件和被过滤邮件,<br/>也可以<span class="c5c green" id="jsSenEmailAgin" style="cursor: pointer;">再次发送验证邮件</span></p>
        </div>
    </div>
    </div>
    <div class="bg" id="dialogBg"></div>
    <header>
        <div class="c-box fff-box">
            <div class="wp header-box">
                <p class="fl hd-tips">慕学在线网,在线学习平台!</p>
                <ul class="fr hd-bar">
                    <li>服务电话:<span>33333333</span></li>
                    <li class="active"><a href="login.html">[登录]</a></li>
                    <li><a href="register.html">[注册]</a></li>
                </ul>
            </div>
        </div>
    </header>
    <section>
        <div class="c-box bg-box">
            <div class="login-box clearfix">
                <div class="hd-login clearfix">
                    <a class="index-logo" href="index.html"></a>
                    <h1>用户登录</h1>
                    <a class="index-font" href="index.html">回到首页</a>
                </div>
                <div class="fl slide">
                    <div class="imgslide">
                        <ul class="imgs">
                                <li><a href=""><img width="483" height="472" src="/static/images/mysql.jpg" /></a></li>
                                <li><a href=""><img width="483" height="472" src="/static/images/mysql.jpg" /></a></li>
                                <li><a href=""><img width="483" height="472" src="/static/images/mysql.jpg" /></a></li>
                        </ul>
                    </div>
                    <div class="unslider-arrow prev"></div>
                    <div class="unslider-arrow next"></div>
                </div>
                <div class="fl form-box">
                    <h2>帐号登录</h2> 
                    <form action="/login/" method="post" autocomplete="off">
                        <input type='hidden' name='csrfmiddlewaretoken' value='mymQDzHWl2REXIfPMg2mJaLqDfaS1sD5' />
                        <div class="form-group marb20 {% if login_form.errors.username %}errorput{% endif %}">
                            <label>&nbsp;&nbsp;</label>
                            <input name="username" id="account_l" type="text" placeholder="手机号/邮箱" />
                        </div>
                        <div class="form-group marb8 {% if login_form.errors.username %}errorput{% endif %}">
                            <label>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
                            <input name="password" id="password_l" type="password" placeholder="请输入您的密码" />
                        </div>
                        <div class="error btns login-form-tips" id="jsLoginTips">
                            {% for key,error in login_form.errors.items %}
                                {{ error }}
                            {% endfor %}
                            {{ msg }}
                        </div>
                         <div class="auto-box marb38">
    
                            <a class="fr" href="forgetpwd.html">忘记密码?</a>
                         </div>
                         <input class="btn btn-green" id="jsLoginBtn" type="submit" value="立即登录 > " />
                    <input type='hidden' name='csrfmiddlewaretoken' value='5I2SlleZJOMUX9QbwYLUIAOshdrdpRcy' />
                        {% csrf_token %}
                    </form>
                    <p class="form-p">没有慕学在线网帐号?<a href="register.html">[立即注册]</a></p>
                </div>
            </div>
        </div>
    </section>
    <script src="/static/js/jquery.min.js" type="text/javascript"></script>
    <script src="/static/js/unslider.js" type="text/javascript"></script>
    <script src="/static/js/login.js"  type="text/javascript"></script>
    </body>
    </html>
    login.html

     显示效果,当不输入用户名,密码小与五位数的时候的提示信息如下:

    上一篇:Django+xadmin打造在线教育平台(一)

    下一篇:Django+xadmin打造在线教育平台(三)

  • 相关阅读:
    RR调度(Round-robin scheduling)简单介绍
    iOS 应用内跳转到系统设置
    Python setup.py和MANIFEST.in文件
    HDU 1017 A Mathematical Curiosity (枚举水题)
    安卓获取软硬件信息并上传给server(Socket实现)
    杭电1869六度分离
    【原创】SM4password算法源代码接口具体解释
    MySQL经常使用命令--create命令使用
    移植opencv2.4.9到itop4412开发板
    Esper epl语句实验
  • 原文地址:https://www.cnblogs.com/derek1184405959/p/8592800.html
Copyright © 2020-2023  润新知