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

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

 

代码

github下载

三、xadmin后台管理

3.1.xadmin的安装

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

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

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

pip install xadmin-django2
 其它版本

如果上面安装提示错误:

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

安装xadmin的命令如下:

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 uinstall 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)
复制代码

1.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
 course/adminx.py
 organization/adminx.py
 operation/adminx.py

此时项目目录结构:

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

 1.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全部代码

再进后台的界面,如下:

(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'

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

添加代码如下:

# users/__init__.py

default_app_config = 'users.apps.UsersConfig'

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

 大功告成

四、完成登录功能

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

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

 (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

 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>
复制代码

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

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

更改代码如下:

 index.html

 (5)增加邮箱登录

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

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

 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

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类

 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})
复制代码
 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>
复制代码

主要修改两处

 login.html

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

原文地址:https://www.cnblogs.com/syq666/p/8715506.html