Django实战(一)——教育网站

WEB开发流程

一、需求分析

二、数据库设计

三、后台管理系统

四、全栈功能实现

五、基础功能补充

六、xadmin的进阶开发

 

django目录

Django实战(一)——教育网站_第1张图片

一级目录:

项目:MxOnline

MxOnline:配置文件

apps:后台业务数据库,路由,view

extra_app:从系统外部导入的app

log:保存系统的日志文件

static:存放前端的静态文件:css、js、img、images

templates:存放前端的网页:html

media:存放用户上传下载的文件

 

二、数据库设计——Django app 设计

1.环境搭建

2.user-用户管理

3.course-课程管理

4.orgnization-机构和教师管理

5.operation-用户操作管理

6.app搜索路径的设置

 

1.环境搭建

1.1 虚拟环境搭建

用到的包是virtualenv和vrtualenvwrapper-win

新建虚拟环境

mkvirtualenv mxonline

激活虚拟环境

workon mxonline

1.2 新建项目

django-admin startproject MxOnline

python manage.py startapp user


1.3 安装包

pip install django==1.9

pip install mysqlclient #连接数据库

pip install pillow #图片的加载

1.4 配置数据库

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': "mxonline",
        'USER': "root",
        'PASSWORD': "888888",
        'HOST':  "127.0.0.1",
    }
}

数据迁移

python manage.py makemigrations

python manage.py migrate

1.5配置 templates

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,

1.6 配置 static

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]

2.user-用户管理

在user.model中设置三个class实现不同的功能

Django实战(一)——教育网站_第2张图片

2.1 UserProfile

继承原有表user中的字段,新填个性化字段

AUTH_USER_MODEL = "user.UserProfile"

 

# _*_ encoding:utf-8 _*_
from django.db import models

from django.contrib.auth.models import AbstractUser #继承原有表user中的字段,新填个性化字段
# Create your models here.

class UserProfile(AbstractUser):
    nick_name = models.CharField(max_length=50, verbose_name=u"昵称", default=u"")
    birthday = models.DateField(verbose_name=u"生日", null=True, blank=True)
    gender = models.CharField(max_length=5, choices=(("male", u"男"), ("female", "女")), default=u"")
    address = models.CharField(max_length=100, default=u"")
    mobile = models.CharField(max_length=11, null=True, blank=True)
    image = models.ImageField(upload_to="image/%y/%m", default=u"image/default.png", max_length=100)

    class Meta:
        verbose_name = "用户信息"
        verbose_name_plural = verbose_name

    def __unicode__(self):
        return self.username

 容易出现的错误,循环引用出错,必须分层,解决方案,分层 operation表示用户行为

Django实战(一)——教育网站_第3张图片Django实战(一)——教育网站_第4张图片

2.2 EmailVerifyRecord 邮箱验证码

class EmailVerifiyRecord(models.Model):
    code = models.CharField(max_length=20, verbose_name=u"验证码")
    email = models.EmailField(max_length=50, verbose_name=u"邮箱")
    send_type = models.CharField(choices=(("regiser", u"注册"), ("forget", u"找回密码")), max_length=10)
    send_time = models.DateField(default=datetime.now)

    class Meta:
        verbose_name = u"邮箱验证码"
        verbose_name_plural = verbose_name

2.3 Banner 轮播图

class Banner(models.Model):
    title = models.CharField(max_length=100, verbose_name=u"标题")
    image = models.ImageField(upload_to="banner/%Y/%m", verbose_name=u"轮播图", max_length=100)
    url = models.URLField(max_length=200, verbose_name=u"访问地址")
    index = models.IntegerField(default=100, verbose_name=u"顺序")
    add_time = models.DateField(default=datetime.now, verbose_name=u"添加时间")

    class Meta:
        verbose_name = u"轮播图"
        verbose_name_plural = verbose_name

3.course-课程管理

Django实战(一)——教育网站_第5张图片

3.1 Course 课程基本信息

class Course(models.Model):
    name = models.CharField(max_length=50, verbose_name=u"课程名")
    desc = models.CharField(max_length=300, verbose_name=u"课程描述")
    detail = models.TextField(verbose_name=u"课程详情")
    degree = models.CharField(choices=(("cj", "初级"), ("zj", "中级"), ("gj", "高级")), max_length=2)
    learn_time = models.IntegerField(default=0, verbose_name=u"学习时长(分钟)")
    student = models.IntegerField(default=0, verbose_name=u"学习人数")
    fav_nums = models.IntegerField(default=0, verbose_name=u"收藏人数")
    image = models.ImageField(upload_to="course/%Y/m", verbose_name=u"封面图", max_length=100)
    click_nums = models.IntegerField(default=0, verbose_name=u"点击数")
    add_time = models.DateField(default=datetime.now,verbose_name=u"添加时间")

    class Meta:
        class Meta:
            verbose_name = u"课程"
            verbose_name_plural = verbose_name

3.2 Lesson 章节信息

class Lesson(models.Model):
    course = models.ForeignKey(Course, verbose_name=u"课程")
    name = models.CharField(max_length=100, verbose_name=u"章节名")
    add_time = models.DateField(default=datetime.now, verbose_name=u"添加时间")

    class Meta:
        class Meta:
            verbose_name = u"章节"
            verbose_name_plural = verbose_name

3.3 Video 视频信息

class Video(models.Model):
    lesson = models.ForeignKey(Lesson, verbose_name=u"章节")
    name = models.CharField(max_length=100, verbose_name=u"视频名")
    add_time = models.DateField(default=datetime.now, verbose_name=u"添加时间")

    class Meta:
        class Meta:
            verbose_name = u"视频"
            verbose_name_plural = verbose_name

3.4 课程资源

class CourseResource(models.Model):
    course = models.ForeignKey(Course, verbose_name=u"课程")
    name = models.CharField(max_length=100, verbose_name=u"名称")
    download = models.FileField(upload_to="course/%Y/%m", verbose_name=u"资源文件", max_length=100)
    add_time = models.DateField(default=datetime.now, verbose_name=u"添加时间")

    class Meta:
        class Meta:
            verbose_name = u"课程资源"
            verbose_name_plural = verbose_name

4.orgnization-机构和教师管理

Django实战(一)——教育网站_第6张图片

 

4.1 CourseOrg 课程机构基本信息

class CourseOrg(models.Model):
    name = models.CharField(max_length=50, verbose_name=u"机构名称")
    desc = models.TextField(verbose_name=u"机构描述")
    click_nums = models.IntegerField(default=0, verbose_name=u"点击数")
    fav_nums = models.IntegerField(default=0, verbose_name=u"收藏数")
    image = models.ImageField(upload_to="org/%Y/m", verbose_name=u"封面图", max_length=100)
    address = models.CharField(max_length=150, verbose_name=u"机构地址")
    city = models.ForeignKey(CityDict, verbose_name=u"所咋城市")
    add_time = models.DateField(default=datetime.now, verbose_name=u"添加时间")

    class Meta:
        class Meta:
            verbose_name = u"课程机构"
            verbose_name_plural = verbose_name

4.2教师信息

class Teacher(models.Model):
    org = models.ForeignKey(CourseOrg, verbose_name=u"所属机构")
    name = models.CharField(max_length=50, verbose_name=u"教师名")
    work_years = models.IntegerField(default=0, verbose_name=u"工作年限")
    work_company = models.CharField(max_length=50, verbose_name=u"就职公司")
    work_position = models.CharField(max_length=50, verbose_name=u"公司职位")
    points = models.CharField(max_length=50, verbose_name=u"教学特点")
    click_nums = models.IntegerField(default=0, verbose_name=u"点击数")
    fav_nums = models.IntegerField(default=0, verbose_name=u"收藏数")
    add_time = models.DateField(default=datetime.now, verbose_name=u"添加时间")

    class Meta:
        class Meta:
            verbose_name = u"教师"
            verbose_name_plural = verbose_name

4.3 城市信息

class CityDict(models.Model):
    name = models.CharField(max_length=20, verbose_name=u"城市")
    desc = models.CharField(max_length=200, verbose_name=u"描述")
    add_time = models.DateField(default=datetime.now, verbose_name=u"添加时间")

    class Meta:
        class Meta:
            verbose_name = u"城市"
            verbose_name_plural = verbose_name

 

5.operation-用户操作管理

Django实战(一)——教育网站_第7张图片

5.1  UserAsk 用户咨询

class UserAsk(models.Model):
    name = models.CharField(max_length=20, verbose_name=u"姓名")
    mobile = models.CharField(max_length=11, verbose_name=u"手机")
    course_name = models.CharField(max_length=20, verbose_name=u"课程名")
    add_time = models.DateTimeField(default=datetime.now, verbose_name=u"添加时间")

    class Meta:
            verbose_name = u"用户咨询"
            verbose_name_plural = verbose_name

5.2 CourseComments 用户评论

class CourseComments(models.Model):
    #课程评论
    user = models.ForeignKey(UserProfile,verbose_name=u"用户")
    course = models.ForeignKey(Course,verbose_name=u"课程")
    comments = models.CharField(max_length=200, verbose_name=u"评论")
    add_time = models.DateTimeField(default=datetime.now, verbose_name=u"添加时间")

    class Meta:
        verbose_name = u"课程评论"
        verbose_name_plural = verbose_name

5.3 UserFavorite 用户收藏

class UserFavorite(models.Model):
    user = models.ForeignKey(UserProfile, verbose_name=u"用户")
    fav_id = models.IntegerField(default=0, verbose_name=u"数据id")
    fav_type = models.IntegerField(choices=((1, "课程"), (2,"课程机构"), (3,"讲师")), default=1, verbose_name=u"收藏类型")

    class Meta:
        verbose_name = u"用户收藏"
        verbose_name_plural = verbose_name

5.4 UserMessage 用户消息

class UserMessage(models.Model):
    user = models.IntegerField(default=0, verbose_name=u"接受用户")
    message = models.CharField(max_length=500, verbose_name=u"消息内容")
    has_read = models.BooleanField(default=False, verbose_name=u"是否已读")
    add_time = models.DateTimeField(default=datetime.now, verbose_name=u"添加时间")

    class Meta:
        verbose_name = u"用户消息"
        verbose_name_plural = verbose_name

5.5UserCourse 用户学习的课程

class UserCourse(models.Model):
    user = models.ForeignKey(UserProfile, verbose_name=u"用户")
    course = models.ForeignKey(Course, verbose_name=u"课程")
    add_time = models.DateTimeField(default=datetime.now, verbose_name=u"添加时间")

    class Meta:
        verbose_name = u"用户课程"
        verbose_name_plural = verbose_name

6.app搜索目录的配置

统一将app放在一个文件夹下便于管理,需要在settings中设置

import os
import sys

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))

三、后台管理系统——django admin

1.权限管理;2.xadmin;3.用户登陆和注册

 

1.权限管理

创建超级用户

python manage.py createsuperuser

#root
#root280010

将后台设置成中文名

LANGUAGE_CODE = 'zh-hans' #中文

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False #本地时间

在管理台上注册用户信息

在admin.py下注册


from .models import UserProfile

class UserProfileAdmin(admin.ModelAdmin):
    pass

admin.site.register(UserProfile, UserProfileAdmin)

Django实战(一)——教育网站_第8张图片

注意:添加用户注意在settings里取消外键约束

'OPTIONS': {
            "init_command": "SET foreign_key_checks = 0;",

2.xadmin【难点+重点】

2.1 xadmin的安装

因为python3.6不支持xadmin的直接安装,所以直接从GitHub获取https://github.com/sshwsfc/xadmin 

1)新建空白文件README.rst 压缩进 zip 中替换掉同名文件

下载后,从错误中可以看到,是文件README.rst 出现了 Unicode 解码错误,这个文件时没有什么用处的,可以新建一个同名的空白文件替换掉。

(2). 然后我们通过 cd 命令切换到该文件夹目录下,通过下面命令安装:

python setup.py install

(3)安装完成后,通过 pip list 可以看到 xadmin 以及相关依赖包都安装完成了。

Django实战(一)——教育网站_第9张图片

(4)安装 import_export的包 

但是你实际运行的时候又会发现,所以继续 安装成功以后,xadmin就算安装成功了。

pip install django-import-export

2.2 xadmin 配置

(1)在settings文件下

INSTALLED_APPS = (
    
    'xadmin',
    'crispy_forms',
    'reversion',
   
)

(2)在url文件下

import xadmin
    # url(r'^admin/', admin.site.urls),
    url(r'^xadmin/', xadmin.site.urls),

2.3更新数据迁移

Django实战(一)——教育网站_第10张图片数据库会更新出一些新的表

2.4 源码的安装

也可以将xadmin拷贝到项目中,修改源码,提供丰富的插件

 

Django实战(一)——教育网站_第11张图片

在settings中设置

import os
import sys

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
sys.path.insert(0, os.path.join(BASE_DIR, 'extra_apps'))

至此xadmin安装成功 ,截图如下(不容易)

xadmin后台管理的特点是:基于BootStrap3,内置功能丰富,强大的插件系统

Django实战(一)——教育网站_第12张图片

2.5 注册models

新建adminx.py

# _*_ coding: utf-8 _*_

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', 'image', 'address', 'city', 'add_time']  # 显示列
    search_fields = [' name', 'desc', 'click_nums', 'fav_nums', 'image', 'address', 'city']             # 搜索
    list_filter = [' name', 'desc', 'click_nums', 'fav_nums', 'image', 'address', 'city', 'add_time']   # 过滤器


class TeacherAdmin(object):
    list_display = ['org', 'name', 'work_years', 'work_company', 'work_position', 'points', 'click_nums', 'fav_nums', 'add_time']   # 显示列
    search_fields = ['org', 'name', 'work_years', 'work_company', 'work_position', 'points', 'click_nums', 'fav_nums']              # 搜索
    list_filter = ['org', 'name', 'work_years', 'work_company', 'work_position', 'points', 'click_nums', 'fav_nums', 'add_time']    # 过滤器



xadmin.site.register(CityDict, CityDictAdmin)
xadmin.site.register(CourseOrg, CourseOrgAdmin)
xadmin.site.register(Teacher, TeacherAdmin)

2.6 xadmin页面的主题修改

(1)显示主题修改页面风格

from xadmin import views

class BaseSetting(object):
    enable_themes = True
    use_bootswatch = True

class GlobalSettings(object):
    site_title = "鹏鹏教学后台管理系统"
    site_footer = "鹏鹏教学网"

xadmin.site.register(views.BaseAdminView, BaseSetting)
xadmin.site.register(views.CommAdminView, GlobalSettings)

(2)导航栏的设置以及更改apps为中文名

apps.py设置

# _*_ coding: utf-8 _*_
from django.apps import AppConfig

class OperationConfig(AppConfig):
    name = 'operation'
    verbose_name = u"用户操作"

__init__.py设置

default_app_config = "user.apps.UserConfig"

效果图

Django实战(一)——教育网站_第13张图片

四、全栈功能实现

    1.用户的登陆和注册,密码找回

    2.课程管理模块——授课机构

    3.课程列表页面——公开课

    4.教师管理模块——授课教师

 

 1.用户的登陆和注册,密码找回

跳转页面login前没有/,将css,js,images导入到static,并配置static

from user.views import LoginView

urlpatterns = [
    url(r'^xadmin/', xadmin.site.urls),

    url('^$', TemplateView.as_view(template_name="index.html"), name="index"),
    url('^login/$', LoginView.as_view(), name="login"),
]
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATICFILES_DIRS = [
    ("css", os.path.join(STATIC_ROOT, 'css')),
    ("images", os.path.join(STATIC_ROOT, 'images').replace('\\', '/')),
    ("js", os.path.join(STATIC_ROOT, 'js')),
    ("img", os.path.join(STATIC_ROOT, 'img')),

1.1登陆后台编写

(0)url.py编写

from django.conf.urls import url
from django.contrib import admin
from django.views.generic import TemplateView
import xadmin
from user.views import LoginView

urlpatterns = [
    url(r'^xadmin/', xadmin.site.urls),

    url('^$', TemplateView.as_view(template_name="index.html"), name="index"),
    url('^login/$', LoginView.as_view(), name="login"),
]

(1)views.py

from django.contrib.auth import authenticate, login
from django.views.generic.base import View
#用户登陆模块
class LoginView(View):
    def get(self, request):
        return render(request, "login.html", {})
    def post(self, request):
        #提交到表单form里面做验证
        login_form = LoginForm(request.POST)
        if login_form.is_valid():
            user_name = request.POST.get("username", "")
            pass_word = request.POST.get("password", "")
            user = authenticate(username=user_name, password=pass_word)
            if user is not None:
                login(request, user)
                return render(request, "index.html")
            else:
                return render(request, "login.html", {"msg": "用户名或密码错误!"})
        else:
            return render(request, "login.html", {"login_form": login_form})

(2)设置email和username同时可以登陆

settings.py配置

# Application definition
AUTHENTICATION_BACKENDS = (
    'user.views.CustomBackend',
)
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q  #并集

from .models import UserProfile

#使用邮箱也可以登陆
class CustomBackend(ModelBackend):
    def authenticate(self, username=None, password=None, **kwargs):
        try:
            user = UserProfile.objects.get(Q(username=username) | Q(email=username))  #完成自定义登陆
            if user.check_password(password):
                return user
        except Exception as e:
            return None

(3)html设置

index.html设置

 {% if request.user.is_authenticated %}
                

服务电话:18582854749

{% else %}
注册 登录 {% endif %}

login.html  如果用户信息出错,则显示错误信息

(4)form表单

用于验证用户名和密码,减轻后台验证的负担

后台新建form.py

#表单,用来验证
from django import forms


class LoginForm(forms.Form):
    username = forms.CharField(required=True)
    password = forms.CharField(required=True, min_length=5)

前端设置信息

#高亮
 

效果图如下:

Django实战(一)——教育网站_第14张图片

 (5)cookies和sesson机制实现自动登陆

cookie在本地浏览器,sesson服务器

 Django实战(一)——教育网站_第15张图片

 1.2 注册后台编写

(1)配置

url.py

url('^register/$', RegisterView.as_view(), name="register"),

index.html

 

register.html,使用这样的方式设置路径,便于以后路径的修改



{% load staticfiles %}

link rel="stylesheet" type="text/css" href="{% static 'css/reset.css' %}">

图片验证码的配置

 #1.pip
 pip install  django-simple-captcha==0.4.6

2.Add captcha to the INSTALLED_APPS in your settings.py


3.Add an entry to your urls.py:

urlpatterns += [
    url(r'^captcha/', include('captcha.urls')),
]
#生成数据库表
python manage.py makemigrations
python manage.py migrate

逻辑设计,view.py

#用户注册
class RegisterView(View):
    def get(self, request):
        register_form = RegisterForm()
        return render(request, "register.html", {'register_form': register_form})

    def post(self, request):
        register_form = RegisterForm(request.POST)
        if register_form.is_valid():
            user_name = request.POST.get("email", "")
            pass_word = request.POST.get("password", "")
            #实例化
            user_profile = UserProfile()
            user_profile.username = user_name
            user_profile.email = user_name
            #用户激活
            user_profile.is_active = False
            #对铭文加密
            user_profile.password = make_password(pass_word)
            user_profile.save()

            send_register_email(user_name, "register")
            return render(request, "login.html")
        else:
            return render(request, "register.html", {"register_form": register_form})

 用户激活的配置

url

 url(r'^active/(?P.*)/$', ActiveUserView.as_view(), name="user_active"),

 settings,注意授权,密码是授权码

#配置邮箱验证
EMAIL_HOST = "smtp.163.com"
EMAIL_PORT = 25
EMAIL_HOST_USER = "[email protected]"
EMAIL_HOST_PASSWORD = "51dk648171lm" #授权码
EMAIL_USE_TLS = False
EMAIL_FROM = "[email protected]" #指明发件人

在app里新建utils文件,建立email_send.py

自动生成随机字符串

# _*_ coding: utf-8 _*_
from random import Random
from django.core.mail import send_mail

from user.models import EmailVerifiyRecord
from MxOnline.settings import EMAIL_FROM


def random_str(randomlength=8):
    str = ''
    chars = 'AaBbCcDdEeFfGgHhIiJjKkMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
    length = len(chars) - 1
    random = Random()
    for i in range(randomlength):
        str += chars[random.randint(0, length)]
    return str


def send_register_email(email, send_type="register"):
    email_record = EmailVerifiyRecord()
    code = random_str(16)
    email_record.code = code
    email_record.email = email
    email_record.send_type = send_type
    email_record.save()

    email_title = ""
    email_body = ""

    if send_type == "register":
        email_title = "鹏鹏在线网注册激活链接"
        email_body = "请点击下面的链接激活你的账号:http://127.0.0.1:8000/active/{0}".format(code)

        send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])
        if send_status:
            pass

1.3找回密码

view.py

#密码找回
class ForgetPwdView(View):
    def get(self, request):
        forget_form = ForgetForm(request.POST)
        return render(request, "forgetpwd.html", {"forget_form": forget_form})
    def post(self, request):
        forget_form = ForgetForm(request.POST)
        if forget_form.is_valid():
            email = request.POST.get("email", "")
            send_register_email(email, "forget")
            return render(request, "send_success.html")
        else:
            return render(request, "forgetpwd.html", {"forget_form": forget_form})


#用户密码激活
class ResetView(View):
    def get(self, request, active_code):
        all_records = EmailVerifiyRecord.objects.filter(code=active_code)
        if all_records:
            for record in all_records:
                email = record.email
                return render(request, "password_reset.html", {"email": email})
        else:
            return render(request, "active_fail.html")
        return render(request, "login.html")
#密码提交
class ModifyPwdView(View):
    def post(self, request):
        #把请求放进表单进行验证
        modify_form = ModifyPwdForm(request.POST)
        if modify_form.is_valid():
            #获取页面数据
            pwd1 = request.POST.get("password1", "")
            pwd2 = request.POST.get("password2", "")
            #取出要修改的用户
            email = request.POST.get("email", "")
            if pwd1 != pwd2:
                return render(request, "password_reset.html", {"email": email, "msg": "密码不一致,请重新输入"})
            user = UserProfile.objects.get(email=email)
            user.password = make_password(pwd1)
            user.save()
            return render(request, "login.html")
        else:
            email = request.POST.get("email", "")
            return render(request, "password_reset.html", {"email": email, "modify_form": modify_form})

 url.py

#密码找回
    url(r'^forget/$', ForgetPwdView.as_view(), name="forget_pwd"),
    url(r'^reset/(?P.*)/$', ResetView.as_view(), name="reset_pwd"),
    url(r'^modify_pwd/$', ModifyPwdView.as_view(), name="modify_pwd"),

password_reset.html

修改密码

已经通过验证,请设置新密码

  • 新 密 码 :
  • 确定密码:
{% csrf_token %}

 

2.课程管理模块——授课机构

templates的继承,注意templates的继承用法,子类替换父类的结构进行扩充。

{% extends 'base.html' %}
{% block title %}课程机构列表 - 慕学在线网{% endblock %}
{% load staticfiles %}
{% block custom_bread %}
    
{% endblock %}

2.1 media的配置

media用于存放用户上传的文件或者图片

 (1)settings.py路径设置

设置 media 路径与目录

import os
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

 settings 中配置模板的处理器

 Templates[
'django.core.context_processors.media',
]

到这一步,仍然不能显示图片,在网页源代码中可以看到有图片的地址。但是仍然需要在 urls 中配置图片地址的路由,让 Django 知道到那个地方去取回图片。

(2)templates中图片地址

data-url="{{ MEDIA_URL }}{{ course_org.image }}

{% for course_org in all_orgs %}

{% endfor %}
(3)url.py配置一个处理静态文件的路由
# -*- coding:utf-8 -*-
from django.views.static import serve 
from MxOnline.settings import MEDIA_ROOT
 urlpatterns = [ 
# 处理 media 信息,用于图片获取 
url(r'^media/(?P.*)$', serve, {"document_root":MEDIA_ROOT}),]

2.2 分页功能

pip install django-pure-pagination

(1)settings.py

INSTALLED_APPS = (
  'pure_pagination',
)

(2)控制页数设定

PAGINATION_SETTINGS = {
    'PAGE_RANGE_DISPLAYED': 10,
    'MARGIN_PAGES_DISPLAYED': 2,

    'SHOW_FIRST_PAGE_WHEN_INVALID': True,
}

http://i.imgur.com/LCqrt.gif

(3)view.py设定

# _*_ encoding:utf-8 _*_
from django.shortcuts import render
from django.views.generic import View
from pure_pagination import Paginator, EmptyPage, PageNotAnInteger #分页
from .models import CourseOrg, CityDict,Teacher

class OrgView(View):
    #课程机构列表功能
    def get(self, request):
        #课程机构
        all_orgs = CourseOrg.objects.all()
        org_nums = all_orgs.count()
        #城市
        all_cities = CityDict.objects.all()
        #对课程机构进行分页
        try:
            page = request.GET.get('page', 1)
        except PageNotAnInteger:
            page = 1
        p = Paginator(all_orgs, 4, request=request)
        orgs = p.page(page)
        
        return render(request, "org-list.html", {
            "all_orgs": orgs,
            "all_cities": all_cities,
            "org_nums": org_nums,
            "city_id": city_id,
            "category": category,
            "hot_orgs": hot_orgs,
            "sort": sort
        })

(4)templates设定org-list.html(分页的设定属于约定俗成的东西)

    {% if all_orgs.has_previous %}
  • 前一页
  • {% endif %} {% for page in all_orgs.pages %} {% if page %} {% ifequal page all_orgs.number %}
  • {{ page }}
  • {% else %}
  • {{ page }}
  • {% endifequal %} {% else %}
  • ...
  • {% endif %} {% endfor %} {% if all_orgs.has_next %}
  • 下一页
  • {% endif %}

2.3 列表筛选功能

(1)所属地区筛选

(2)机构类别筛选

view.py

 #取出筛选城市
city_id = request.GET.get('city', "")
if city_id:
    all_orgs = all_orgs.filter(city_id=int(city_id))
#机构类别筛选
category = request.GET.get('ct', '')
if category:
    all_orgs = all_orgs.filter(category=category)
#每一列的总数
org_nums = all_orgs.count()

org-list.html

注意:templates的用法,机构类别的筛选和所在地区的筛选杂糅到一块,直接相互关联!

机构类别

所在地区

更多
全部 {% for city in all_cities %} {{ city.name }} {% endfor %}

2.4 列表排序功能

(1)授课机构排名

(2)所属列表排序(学习人数和课程数)

views.py

#课程机构排名
hot_orgs = all_orgs.order_by("click_nums")[:5]
#所属列表排序
sort = request.GET.get('sort', "")
if sort:
    if sort == "students":
        all_orgs = all_orgs.order_by("-students")
    elif sort == "courses":
        all_orgs = all_orgs.order_by("-course_nums")

org-list.html 各种情况下的排序杂糅到一块,一定相互关联!active高亮

#授课机构排序
授课机构排名
{% for current_org in hot_orgs %}
{{ forloop.counter }}

{{ current_org.name }}

{{ current_org.address }}

{% endfor %}
#所属列的排序

2.5 使用ajax异步提交表单(Model.Form提交表单)

Django实战(一)——教育网站_第16张图片

在总url里面设置路由分发

 #课程机构url配置
url(r'^org/', include('organization.urls', namespace="org")),

在organization里新建子路由url

from django.conf.urls import url, include

from .views import OrgView, AddUserAskView

urlpatterns = [
    # 课程机构列表页
    url(r'^list/$', OrgView.as_view(), name="org_list"),
    url(r'^add_ask', AddUserAskView.as_view(), name="add_ask")
]

view.py

使用HttpResponse返回json数据

from django.http import HttpResponse
from .forms import UserAskForm

#表单的提交
class AddUserAskView(View):
    #用户添加咨询
    def post(self, request):
        userask_form = UserAskForm(request.POST)
        if userask_form.is_valid():

            user_ask = userask_form.save(commit=True)
            #返回异步操作ajax/json
            return HttpResponse("{'status':'success'}", content_type='application/json')
        else:
            return HttpResponse("{'status':'fail','msg':'添加出错'}", content_type='application/json')

在organization里面新建forms.py,使用ModelForm验证。

# _*_ coding: utf-8 _*_

import re
from django import forms
from operation.models import UserAsk

class UserAskForm(forms.ModelForm):

    class Meta:
        model = UserAsk
        fields = ['name', 'mobile', 'course_name']

    def clean_mobile(self):
        #验证手机号码是否合法
        mobile = self.cleaned_data['mobile']
        REGEX_MOBILE = "^1[358]\d{9}$|^147\d{8}$|^176\d{8}$"
        p = re.compile(REGEX_MOBILE)
        if p.match(mobile):
            return mobile
        else:
            raise forms.ValidationError(u"手机号码非法", code="mobile_invalid")

重点在templates里面通过js的框架jquery配置ajax,实现数据的异步传输

{% block custom_js %}

{% endblock %}

数据库结果

Django实战(一)——教育网站_第17张图片

2.6. 机构详情页功能实现

(1)机构首页

(2)机构课程

(3)机构介绍

(4)机构讲师

url.py

from django.conf.urls import url, include

from .views import OrgView, AddUserAskView, OrgHomeView, OrgCourseView, OrgDescView, OrgTeacherView

urlpatterns = [
    # 课程机构列表页
    url(r'^list/$', OrgView.as_view(), name="org_list"),
    url(r'^add_ask', AddUserAskView.as_view(), name="add_ask"),
    url(r'^home/(?P\d+)/$', OrgHomeView.as_view(), name="org_home"),
    url(r'^course/(?P\d+)/$', OrgCourseView.as_view(), name="org_course"),
    url(r'^desc/(?P\d+)/$', OrgDescView.as_view(), name="org_desc"),
    url(r'^teacher/(?P\d+)/$', OrgTeacherView.as_view(), name="org_teacher"),

]

view.py

#机构详情页
class OrgHomeView(View):
    #机构首页
    def get(self, request, org_id):
        current_page = "home"
        course_org = CourseOrg.objects.get(id=int(org_id))
        #用户收藏的展示(已收藏/收藏)
        has_fav = False
        if request.user.is_authenticated():
            if UserFavorite.objects.filter(user=request.user, fav_id=course_org.id, fav_type=2):
                has_fav = True

        all_courses = course_org.course_set.all()
        all_teachers = course_org.teacher_set.all()
        return render(request, 'org-detail-homepage.html', {
            'all_courses': all_courses,
            'all_teachers': all_teachers,
            'course_org': course_org,
            'current_page': current_page,
            'has_fav': has_fav
        })

class OrgCourseView(View):
    #机构课程介绍页
    def get(self, request, org_id):
        current_page = "course"
        course_org = CourseOrg.objects.get(id=int(org_id))
        has_fav = False
        if request.user.is_authenticated():
            if UserFavorite.objects.filter(user=request.user, fav_id=course_org.id, fav_type=2):
                has_fav = True

        all_courses = course_org.course_set.all()
        return render(request, 'org-detail-course.html', {
            'all_courses': all_courses,
            'course_org': course_org,
            'current_page': current_page,
            'has_fav': has_fav
        })

class OrgDescView(View):
    # 机构介绍页
    def get(self, request, org_id):
        current_page = "desc"
        course_org = CourseOrg.objects.get(id=int(org_id))
        has_fav = False
        if request.user.is_authenticated():
            if UserFavorite.objects.filter(user=request.user, fav_id=course_org.id, fav_type=2):
                has_fav = True

        all_courses = course_org.course_set.all()
        return render(request, 'org-detail-desc.html', {
            'all_courses': all_courses,
            'course_org': course_org,
            'current_page': current_page,
            'has_fav': has_fav
        })

class OrgTeacherView(View):
    #机构教师页
    def get(self, request, org_id):
        current_page = "teacher"
        course_org = CourseOrg.objects.get(id=int(org_id))
        has_fav = False
        if request.user.is_authenticated():
            if UserFavorite.objects.filter(user=request.user, fav_id=course_org.id, fav_type=2):
                has_fav = True

        all_teachers = course_org.teacher_set.all()
        return render(request, 'org-detail-teachers.html', {
            'all_teachers': all_teachers,
            'course_org': course_org,
            'current_page': current_page,
            'has_fav': has_fav
        })

 templates 新建org-list.html作为机构母页

新建母页org_base.html



{% load staticfiles %}

	
    
    
	{% block title %}机构首页-慕学网{% endblock %}
	
	
	
    
    {% block custom_css %}{% endblock %}
    
    
    {% block custom_js %}{% endblock %}


{% if request.user.is_authenticated %}

服务电话:18582854749

{% else %}
注册 登录 {% endif %}

慕课网

推荐指数: 5.0

{% if has_fav %}已收藏{% else %}收藏{% endif %}
{% block right_form %} {% endblock %}

(1)机构首页org-detail-homepage.html

{% extends 'org_base.html' %}
{% block title %}机构首页{% endblock %}
{% block page_path %}机构首页{% endblock %}
{% block right_form %}

全部课程

查看更多 >
{% for course in all_courses %}

{{ course.name }}

课时:{{ course.learn_time }} 参加人数:{{ course.students }}
{{ course.course_org.name }} {{ course.fav_nums }}
{% endfor %}

机构教师

查看更多 >
{% for teacher in all_teachers %}
{% endfor %}

机构介绍

查看更多 >
   {{ course_org.desc }}[查看更多]
{% endblock %}

(2)机构课程页org-detail-course.html

{% extends 'org_base.html' %}
{% block title %}机构课程列表页{% endblock %}
{% block page_path %}机构课程{% endblock %}
{% block right_form %}

机构课程

{% for course in all_courses %}

{{ course.name }}

课时:{{ course.learn_time }} 学习人数:{{ course.students }}
{{ course.course_org.name }} {{ course.fav_nums }}
{% endfor %}
{% endblock %}

(3)机构介绍页org-detail-desc.html

{% extends 'org_base.html' %}
{% block title %}机构介绍页{% endblock %}
{% block page_path %}机构介绍{% endblock %}
{% block right_form %}

机构介绍

{{ course_org.desc }}

{% endblock %}

(4)机构讲师页 org-detail-teacher.html

{% extends 'org_base.html' %}
{% block title %}教师列表{% endblock %}
{% block page_path %}机构讲师{% endblock %}
{% block right_form %}

机构讲师

{% for teacher in all_teachers %}

{{ teacher.name }}已认证

  • 工作年限:{{ teacher.work_years }}
  • 课程数:3
{% endfor %}
{% endblock %}

2.7 用户收藏/分享功能实现

url.py

#用户收藏
url(r'^add_fav/$', AddFavView.as_view(), name="add_fav"),

view.py

#用户收藏/取消收藏
class AddFavView(View):
    def post(self, request):
        fav_id = request.POST.get('fav_id', 0)
        fav_type = request.POST.get('fav_type', 0)
        #判断用户登陆状态
        if not request.user.is_authenticated():
            return HttpResponse('{"status":"fail", "msg":"用户未登录"}', content_type='application/json')

        exist_records = UserFavorite.objects.filter(user=request.user, fav_id=int(fav_id), fav_type=int(fav_type))
        if exist_records:
            #如果记录已经存在,则表示用户取消收藏
            exist_records.delete()
            #课程-1操作
            if int(fav_type) == 1:
                course = Course.objects.get(id=int(fav_id))
                course.fav_nums -= 1
                if course.fav_nums < 0:
                    course.fav_nums = 0
                course.save()
            #课程机构-1操作
            elif int(fav_type) == 2:
                course_org = CourseOrg.objects.get(id=int(fav_id))
                course_org.fav_nums -= 1
                if course_org.fav_nums < 0:
                    course_org.fav_nums = 0
                course_org.save()
            #教师-1操作
            elif int(fav_type) == 3:
                teacher = Teacher.objects.get(id=int(fav_id))
                teacher.fav_nums -= 1
                if teacher.fav_nums < 0:
                    teacher.fav_nums = 0
                teacher.save()

            return HttpResponse('{"status":"success", "msg":"收藏"}', content_type='application/json')
        else:
            user_fav = UserFavorite()
            if int(fav_id) > 0 and int(fav_type) > 0:
                user_fav.user = request.user
                user_fav.fav_id = int(fav_id)
                user_fav.fav_type = int(fav_type)
                user_fav.save()
                #收藏
                # 课程+1操作
                if int(fav_type) == 1:
                    course = Course.objects.get(id=int(fav_id))
                    course.fav_nums += 1
                    course.save()
                # 课程机构+1操作
                elif int(fav_type) == 2:
                    course_org = CourseOrg.objects.get(id=int(fav_id))
                    course_org.fav_nums += 1
                    course_org.save()
                # 教师+1操作
                elif int(fav_type) == 3:
                    teacher = Teacher.objects.get(id=int(fav_id))
                    teacher.fav_nums += 1
                    teacher.save()

                return HttpResponse('{"status":"success", "msg":"已收藏"}', content_type='application/json')
            else:
                return HttpResponse('{"status":"fail", "msg":"收藏出错"}', content_type='application/json')

ajax实现异步收藏/分享


function add_fav(current_elem, fav_id, fav_type){
    $.ajax({
        cache: false,
        type: "POST",
        url:'{% url "org:add_fav" %}',
        data:{'fav_id':fav_id, 'fav_type':fav_type},
        async: true,
        beforeSend:function(xhr, settings){
            xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token }}");
        },
        success: function(data) {
            if(data.status == 'fail'){
                if(data.msg == '用户未登录'){
                    window.location.href="/login/";
                }else{
                    current_elem.text(data.msg)
                }

            }else if(data.status == 'success'){
                current_elem.text(data.msg)
            }
        },
    });
}

$('.collectionbtn').on('click', function(){
    add_fav($(this), {{ course_org.id }}, 2);
});

$(function(){
    var $precision = $('.precision'),
        score = $precision.attr('data-star-scope'),
        option =  {
                    half		: true,
                    path		: '{% static 'images/' %}',
                    precision	: true,
                    size		: 24,
                    starOff		: 'g_star.png',
                    starOn		: 'r_star.png',
                    starHalf	: 'h_star.png',
                    hints       : ['极差', '差', '一般', '好评', '非常满意'],
                    noRatedMsg  : '暂时还未获得评价!',
                    readOnly    : true,
                    score       : score
                };
    $precision.raty(option);

    $('.jsFavBtn').on('click', function(){
        var type = $(this).attr('data-fav-type');
        if(type == '1'){
            favPraise($(this), 'fav' ,1 , '收藏');

        }else if(type == '3'){
            favPraise($(this), 'fav' ,3 );

        }else if(type == '11'){
            favPraise($(this), 'pra', 1);

        }else if(type == '4'){
            favPraise($(this), 'fav' ,4 );

        }
    });
})

 

3. 课程列表页面——公开课

    3.1 课程列表页面

    3.2 课程详情页面

    3.3 课程章节信息

    3.4 课程评论页面

    3.5 视频播放页面

 

新建mixin_utils.py,做登陆验证,如果没有登陆则返回登陆页面,才可以进行课程的一系列操作

# _*_ coding: utf-8 _*_
#做登陆验证
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator


class LoginRequiredMixin(object):

    @method_decorator(login_required(login_url='/login/'))
    def dispatch(self, request, *args, **kwargs):
        return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)

 

3.1 课程列表页面

(1)热门机构排序

(2)课程列表排序 -表示降序排列

(3)分页

(4)热门课程推荐(见3.2)

url.py

urlpatterns = [
    # 课程列表页
    url(r'^list/$', CourseListView.as_view(), name="course_list"),
]

views.py

#公开课列表页面
class CourseListView(View):
    def get(self, request):
        all_courses = Course.objects.all().order_by("-add_time")

        #热门机构排序
        hot_courses = Course.objects.all().order_by("click_nums")[:3]

        # 课程列表排序   -表示降序排列
        sort = request.GET.get('sort', "")
        if sort:
            if sort == "student":
                all_courses = all_courses.order_by("-student")
            elif sort == "hot":
                all_courses = all_courses.order_by("-click_nums")

        # 对课程进行分页
        try:
            page = request.GET.get('page', 1)
        except PageNotAnInteger:
            page = 1
        p = Paginator(all_courses, 6, request=request)
        courses = p.page(page)

        return render(request, 'course-list.html', {
            "all_courses": courses,
            "sort": sort,
            "hot_courses": hot_courses

        })

templates

course-list.html

{% block content %}
	
{% for course in all_courses.object_list %}

{{ course.name }}

时长:{{ course.learn_time }} 学习人数:{{ course.student }}
来自{{ course.course_org.name }} {{ course.fav_nums }}
{% endfor %}
    {% if all_courses.has_previous %}
  • 前一页
  • {% endif %} {% for page in all_courses.pages %} {% if page %} {% ifequal page all_courses.number %}
  • {{ page }}
  • {% else %}
  • {{ page }}
  • {% endifequal %} {% else %}
  • ...
  • {% endif %} {% endfor %} {% if all_courses.has_next %}
  • 下一页
  • {% endif %}
热门课程推荐
{% for hot_course in hot_courses %}

{{ hot_course.name }}

难度:{{ hot_course.get_degree_display }}
{% endfor %}
{% endblock %}

3.2 课程详情页

(1)收藏功能——利用ajax

url.py

urlpatterns = [
    #课程详情页
    url(r'^detail/(?P\d+)/$', CourseDetailView.as_view(), name="course_detail"),
]

views.py

#课程详情页
class CourseDetailView(View):
    def get(self, request, course_id):
        course = Course.objects.get(id=int(course_id))

        #增加课程点击数
        course.click_nums += 1
        course.save()

        #收藏
        has_fav_course = False
        has_fav_org = False

        if request.user.is_authenticated():
            if UserFavorite.objects.filter(user=request.user, fav_id=course.id, fav_type=1):
                has_fav_course = True
            if UserFavorite.objects.filter(user=request.user, fav_id=course.course_org.id, fav_type=2):
                has_fav_org = True

        #课程推荐
        tag = course.tag
        if tag:
            relate_courses = Course.objects.filter(tag=tag)[:2]
        else:
            relate_courses = []

        return render(request, 'course-detail.html', {
            "course": course,
            "relate_courses": relate_courses,
            "has_fav_course": has_fav_course,
            "has_fav_org": has_fav_org
        })

templates

主要实现左右两个收藏功能

course-detail.html

授课机构

世界名校,课程权威

{{ course.course_org.name }}

{% if has_fav_org %}已收藏{% else %}收藏{% endif %}

ajax配置——实现异步收藏

{% block custom_js %}

{% endblock %}

3.3 课程章节信息

url.py

urlpatterns = [
    #课程章节信息
    url(r'^info/(?P\d+)/$', CourseInfoView.as_view(), name="course_info"),
]

views.py

#课程章节信息页面
class CourseInfoView(LoginRequiredMixin ,View):
    def get(self, request, course_id):
        course = Course.objects.get(id=int(course_id))
        #点击课程+1
        course.student += 1
        course.save()

    # 获取学过该用户学过其他的所有课程
        #查询用户是否已经关联了该课程
        user_courses_ralate = UserCourse.objects.filter(user=request.user, course=course)
        if not user_courses_ralate:
            user_course_ralate = UserCourse(user=request.user, course=course)
            user_course_ralate.save()
        user_courses = UserCourse.objects.filter(course=course)
        user_ids = [user_course.user.id for user_course in user_courses]
        all_user_courses = UserCourse.objects.filter(user_id__in=user_ids)
        #取出所有课程id
        course_ids = [user_course.course.id for user_course in all_user_courses]
        relate_courses = Course.objects.filter(id__in=course_ids).order_by("-click_nums")[:5]

        all_courses = CourseResource.objects.filter(course=course)

        return render(request, 'course-video.html',  {
            "course": course,
            "course_resources": all_courses,
            "relate_courses": relate_courses,

        })

templates

course-video.html

{% block content %}
    

{{ course.name }}

{{ course.get_degree_display }} 难度
{{ course.learn_time }}分钟 时长
{{ course.student }} 学习人数
{% for lesson in course.get_course_lesson %}

{{ lesson.name }}

{% endfor %}

资料下载

    {% for course_resource in course_resources %}
  • {{ course_resource.name }} 下载
  • {% endfor %}

讲师提示

{{ course.teacher.name }} {{ course.teacher.work_position }}
课程须知
{{ course.youneed_know }}
老师告诉你能学到什么?
{{ course.teacher_tell }}

该课的同学还学过

{% endblock %}

3.4 课程评论页面

  (1)课程评论信息的展示页面

(2)用户添加课程评论【重要】

效果图

Django实战(一)——教育网站_第18张图片

url.py

urlpatterns = [
    #课程评论
    url(r'^comment/(?P\d+)/$', CommentsView.as_view(), name="course_comment"),
    #添加课程评论
    url(r'^add_comment/$', AddCommentsView.as_view(), name="add_comment"),
]

views.py

#课程评论信息
class CommentsView(LoginRequiredMixin ,View):
    def get(self, request, course_id):
        course = Course.objects.get(id=int(course_id))
        all_courses = CourseResource.objects.filter(course=course)
        all_comments = CourseComments.objects.all()

        return render(request, 'course-comment.html', {
            "course": course,
            "course_resources": all_courses,
            "all_comments": all_comments,

        })

#添加评论
class AddCommentsView(View):
    '''用户评论'''
    def post(self, request):
        if not request.user.is_authenticated:
            # 未登录时返回json提示未登录,跳转到登录页面是在ajax中做的
            return HttpResponse('{"status":"fail", "msg":"用户未登录"}', content_type='application/json')
        course_id = request.POST.get("course_id", 0)
        comments = request.POST.get("comments", "")
        if int(course_id) > 0 and comments:
            # 实例化一个course_comments对象
            course_comments = CourseComments()
            # 获取评论的是哪门课程
            course = Course.objects.get(id = int(course_id))
            # 分别把评论的课程、评论的内容和评论的用户保存到数据库
            course_comments.course = course
            course_comments.comments = comments
            course_comments.user = request.user
            course_comments.save()
            return HttpResponse('{"status":"success", "msg":"评论成功"}', content_type='application/json')
        else:
            return HttpResponse('{"status":"fail", "msg":"评论失败"}', content_type='application/json')

templates

course-comment.html

    {% for user_comment in all_comments %}
  • {{ user_comment.user.username }}

    {{ user_comment.comments }}

  • {% endfor %}

ajax异步添加评论

{% block custom_js %}
    
{% endblock %}

 3.5 视频播放页面

url.py


urlpatterns = [
    #视频
    url(r'^video/(?P\d+)/$', VideoPlayView.as_view(), name="video_play"),

]

views.py

#视频播放页面
class VideoPlayView(View):
    def get(self, request, video_id):
        video = Video.objects.get(id=int(video_id))
        course = video.lesson.course
        course.student += 1
        course.save()
        #查询用户是否已经关联了该课程
        user_courses_ralate = UserCourse.objects.filter(user=request.user, course=course)
        if not user_courses_ralate:
            user_course_ralate = UserCourse(user=request.user, course=course)
            user_course_ralate.save()
        user_courses = UserCourse.objects.filter(course=course)
        user_ids = [user_course.user.id for user_course in user_courses]
        all_user_courses = UserCourse.objects.filter(user_id__in=user_ids)
        #取出所有课程id
        course_ids = [user_course.course.id for user_course in all_user_courses]
        relate_courses = Course.objects.filter(id__in=course_ids).order_by("-click_nums")[:5]

        all_courses = CourseResource.objects.filter(course=course)

        return render(request, 'course-play.html', {
            "course": course,
            "course_resources": all_courses,
            "relate_courses": relate_courses,
            "video": video,
        })

templates

course-play.html

使用js提供的插件video.js,实现播放功能,去官网下载。在七牛云上上传视频,得到点击视频的链接。

{% block custom_css %}
   
    
{% endblock %}
{% block custom_js %}
    
{% endblock %}

4.教师管理模块——授课教师

    4.1课程讲师页面

    4.2课程详情页面

4.1课程讲师页面

url.py

urlpatterns = [
    #讲师列表页
    url(r'^teacher/list/$', TeacherListView.as_view(), name="teacher_list"),
]

views.py

#课程讲师列表页
class TeacherListView(View):

    def get(self, request):
        all_teachers = Teacher.objects.all()

        # 所属列表人气排序
        sort = request.GET.get('sort', "")
        if sort:
            if sort == "hot":
                all_teachers = all_teachers.order_by("-click_nums")

        #讲师排行榜
        sorted_teacher = Teacher.objects.all().order_by("-click_nums")[:5]

        # 对讲师进行分页
        try:
            page = request.GET.get('page', 1)
        except PageNotAnInteger:
            page = 1
        p = Paginator(all_teachers, 4, request=request)
        teachers = p.page(page)

        return render(request, "teachers-list.html", {
            "all_teachers": teachers,
            "sorted_teacher": sorted_teacher,
            "sort": sort,
        })

  4.2课程详情页面

url.py

urlpatterns = [
    #讲师详情页
    url(r'^teacher/detail/(?P\d+)/$', TeacherDetailView.as_view(), name="teacher_detail"),
]

views.py

class TeacherDetailView(View):

    def get(self, request, teacher_id):
        teacher = all_teachers = Teacher.objects.get(id=int(teacher_id))
        teacher.click_nums += 1
        teacher.save()
        all_courses = Course.objects.filter(teacher=teacher)

        #收藏
        has_teacher_faved = False
        if UserFavorite.objects.filter(user=request.user, fav_type=3, fav_id=teacher.id):
            has_teacher_faved = True

        has_org_faved = False
        if UserFavorite.objects.filter(user=request.user, fav_type=2, fav_id=teacher.org.id):
            has_org_faved = True


        # 讲师排行榜
        sorted_teacher = Teacher.objects.all().order_by("-click_nums")[:5]

        return render(request, "teacher-detail.html", {
            "teacher": teacher,
            "all_courses": all_courses,
            "sorted_teacher": sorted_teacher,
            "has_teacher_faved": has_teacher_faved,
            "has_org_faved": has_org_faved,
        })

ajax收藏

五、基础功能补充

    5.1配置全局导航

    5.2全局搜索功能

    5.3个人信息展示

    5.4 配置首页index

    5.5配置错误提示页面405和505

5.1配置全局导航

 

5.2全局搜索功能

from django.db.models import Q  #并集

#课程全局搜索
search_keywords = request.GET.get('keywords', "")
if search_keywords:
all_courses = all_courses.filter(Q(name__icontains=search_keywords) | Q(desc__icontains=search_keywords) | Q(detail__icontains=search_keywords))

# 机构全局搜索
search_keywords = request.GET.get('keywords', "")
if search_keywords:
all_orgs = all_orgs.filter(Q(name__icontains=search_keywords) | Q(desc__icontains=search_keywords))

#讲师全局搜索
search_keywords = request.GET.get('keywords', "")
if search_keywords:
all_teachers = all_teachers.filter(Q(name__icontains=search_keywords) |
Q(work_company__icontains=search_keywords))

在全局搜索出现了一个bug,当我在给某一张表加上外键搜索的时候,会出现 TypeError: Related Field got invalid lookup: icontains。

问题原因:

  a 表关联 b表,也就是说a表有外键关联b表,我们只需要在 b表中admin注册时候在admin类中添加外键搜索功能,那么在创建a表数据的时候,可以通过搜索拿到结果,那么这个错误来源不是这个字段 relfield_style 的错误,而是xadmin本身那个搜索框 search_fields的错误,因为b表中又有外键关联c表,b表admin中把外键添加到 search_fields 搜索字段中,从而产生搜索错误,search_fields 是声明搜索框搜索哪些字段

那,如何解决?显然org是外键,添加到admin出现错误。去掉外键

  只需要在每个表的admin中把过滤字段中把这个外键字段取消搜索注册,也就是说,外键字段不应该添加到 search_fields 搜索功能中, 而 relfield_style 字段应该写在 被关联的表的admin中。

 

5.3个人信息展示

       5.3.1个人资料

       5.3.2我的课程

       5.3.3我的收藏

       5.3.4我的消息

 

5.3.1个人资料

Django实战(一)——教育网站_第19张图片

#用户中心
url(r'^users/', include('user.urls', namespace="users"))

   views.py

#用户中心
class UserinfoView(LoginRequiredMixin, View):

    def get(self, request):
        return render(request, "usercenter-info.html", {})

    def post(self, request):
        #instance=request.user指明修改form、不是新增user
        user_info_form = UserInfoForm(request.POST, instance=request.user)
        if user_info_form.is_valid():
            user_info_form.save()
            return HttpResponse('{"status":"success"}', content_type='application/json')

        else:
            return HttpResponse(json.dumps(user_info_form.errors), content_type='application/json')

usercenter-info.html


					
  • 昵       称:
  • 生       日:
  • 性       别:
  • 地       址:
  • 手  机  号:
  • 邮       箱: [修改]
{% csrf_token %}

ajax配置

    //保存个人资料
    $('#jsEditUserBtn').on('click', function(){
        var _self = $(this),
            $jsEditUserForm = $('#jsEditUserForm')
            verify = verifySubmit(
            [
                {id: '#nick_name', tips: Dml.Msg.epNickName, require: true}
            ]
        );
        if(!verify){
           return;
        }
        $.ajax({
            cache: false,
            type: 'post',
            dataType:'json',
            url:"/users/info/",
            data:$jsEditUserForm.serialize(),
            async: true,
            beforeSend:function(XMLHttpRequest){
                _self.val("保存中...");
                _self.attr('disabled',true);
            },
            success: function(data) {
                if(data.nick_name){
                    _showValidateError($('#nick_name'), data.nick_name);
                }else if(data.birthday){
                   _showValidateError($('#birth_day'), data.birthday);
                }else if(data.address){
                   _showValidateError($('#address'), data.address);
                }else if(data.status == "failure"){
                     Dml.fun.showTipsDialog({
                        title: '保存失败',
                        h2: data.msg
                    });
                }else if(data.status == "success"){
                    Dml.fun.showTipsDialog({
                        title: '保存成功',
                        h2: '个人信息修改成功!'
                    });
                    setTimeout(function(){window.location.href = window.location.href;},1500);
                }
            },
            complete: function(XMLHttpRequest){
                _self.val("保存");
                _self.removeAttr("disabled");
            }
        });
    });


});

   (1)用户修改头像

   (2)修改密码功能

   (3)修改邮箱功能

 

(1)用户修改头像(2)修改密码功能

配置url.py

urlpatterns = [
    # 用户中心
    url(r'^info/$', UserinfoView.as_view(), name="user_info"),
    #用户上传头像
    url(r'^image/upload/$', UploadImageView.as_view(), name="image_upload"),
    #用户个人中心修改密码
    url(r'^update/pwd/$', UpdatePwdView.as_view(), name="update_pwd"),

]

新建一个form


class UploadImageForm(forms.ModelForm):
    """
    处理用户上传头像
    """
    class Meta:
        model = UserProfile
        fields = ['image']

配置views.py

#用户修改头像
class UploadImageView(LoginRequiredMixin, View):

    def post(self, request):
        image_form = UploadImageForm(request.POST, request.FILES, instance=request.user)
        if image_form.is_valid():
            image_form.save()
            return HttpResponse('{"status":"success"}', content_type='application/json')
        else:
            return HttpResponse('{"status":"fail"}', content_type='application/json')


#在个人中心修改密码
class UpdatePwdView(View):

    def post(self, request):
        modify_form = ModifyPwdForm(request.POST)
        if modify_form.is_valid():
            pwd1 = request.POST.get("password1", "")
            pwd2 = request.POST.get("password2", "")
            if pwd1 != pwd2:
                return HttpResponse('{"status":"fail", "msg": "密码不一致"}', content_type='application/json')
            user = request.user
            user.password = make_password(pwd2)
            user.save()

            return HttpResponse('{"status":"success"}', content_type='application/json')
        else:
            return HttpResponse(json.dumps(modify_form.errors), content_type='application/json')

配置usercenter-info.html

{% block right_content %}
    

个人信息

{% csrf_token %}
  • 昵       称:
  • 生       日:
  • 性       别:
  • 地       址:
  • 手  机  号:
  • 邮       箱: [修改]
{% csrf_token %}
{% endblock %}

ajax配置

$(function(){
    //个人资料修改密码
    $('#jsUserResetPwd').on('click', function(){
        Dml.fun.showDialog('#jsResetDialog', '#jsResetPwdTips');
    });

    $('#jsResetPwdBtn').click(function(){
        $.ajax({
            cache: false,
            type: "POST",
            dataType:'json',
            url:"/users/update/pwd/",
            data:$('#jsResetPwdForm').serialize(),
            async: true,
            success: function(data) {
                if(data.password1){
                    Dml.fun.showValidateError($("#pwd"), data.password1);
                }else if(data.password2){
                    Dml.fun.showValidateError($("#repwd"), data.password2);
                }else if(data.status == "success"){
                    Dml.fun.showTipsDialog({
                        title:'提交成功',
                        h2:'修改密码成功,请重新登录!',
                    });
                    Dml.fun.winReload();
                }else if(data.msg){
                    Dml.fun.showValidateError($("#pwd"), data.msg);
                    Dml.fun.showValidateError($("#repwd"), data.msg);
                }
            }
        });
    });


    //个人资料头像
    $('.js-img-up').uploadPreview({ Img: ".js-img-show", Width: 94, Height: 94 ,Callback:function(){
    $('#jsAvatarForm').submit();
    }});

    $('.changeemai_btn').click(function(){
        Dml.fun.showDialog('#jsChangeEmailDialog', '#jsChangePhoneTips' ,'jsChangeEmailTips');
    });
    $('#jsChangeEmailCodeBtn').on('click', function(){
        sendCodeChangeEmail($(this));
    });
    $('#jsChangeEmailBtn').on('click', function(){
        changeEmailSubmit($(this));
    });

(3)修改邮箱功能  (发送验证码,完成验证)

配置url.py

urlpatterns = [
    # 用户中心
    url(r'^info/$', UserinfoView.as_view(), name="user_info"),
    #发送邮箱验证码
    url(r'^sendemail_code/$', SendEmailCodeView.as_view(), name="sendemail_code"),
    #修改邮箱
    url(r'^update_email/$', UpdateEmailView.as_view(), name="update_email"),

]

views.py

#个人中心发送邮箱验证码
class SendEmailCodeView(LoginRequiredMixin, View):

    def get(self, request):
        email = request.GET.get('email', '')

        if UserProfile.objects.filter(email=email):
            return HttpResponse('{"email":"邮箱已存在"}', content_type='application/json')
        send_register_email(email, "update_email")
        return HttpResponse('{"status":"success"}', content_type='application/json')

#修改个人邮箱,完成验证
class UpdateEmailView(LoginRequiredMixin, View):

    def post(self, request):
        email = request.POST.get('email', '')
        code = request.POST.get('code', '')

        existed_records = EmailVerifiyRecord.objects.filter(email=email, code=code, send_type='update_email')

        if existed_records:
            user = request.user
            user.email = email
            user.save()
            return HttpResponse('{"status":"success"}', content_type='application/json')
        else:
            return HttpResponse('{"email":"验证码出错"}', content_type='application/json')

配置usercenter-base.html

    

修改邮箱

请输入新的邮箱地址

请输入...
{% csrf_token %}

配置ajax

//修改个人中心邮箱验证码
function sendCodeChangeEmail($btn){
    var verify = verifyDialogSubmit(
        [
          {id: '#jsChangeEmail', tips: Dml.Msg.epMail, errorTips: Dml.Msg.erMail, regName: 'email', require: true}
        ]
    );
    if(!verify){
       return;
    }
    $.ajax({
        cache: false,
        type: "get",
        dataType:'json',
        url:"/users/sendemail_code/",
        data:$('#jsChangeEmailForm').serialize(),
        async: true,
        beforeSend:function(XMLHttpRequest){
            $btn.val("发送中...");
            $btn.attr('disabled',true);
        },
        success: function(data){
            if(data.email){
                Dml.fun.showValidateError($('#jsChangeEmail'), data.email);
            }else if(data.status == 'success'){
                Dml.fun.showErrorTips($('#jsChangeEmailTips'), "邮箱验证码已发送");
            }else if(data.status == 'failure'){
                 Dml.fun.showValidateError($('#jsChangeEmail'), "邮箱验证码发送失败");
            }else if(data.status == 'success'){
            }
        },
        complete: function(XMLHttpRequest){
            $btn.val("获取验证码");
            $btn.removeAttr("disabled");
        }
    });

}

//个人资料邮箱修改
function changeEmailSubmit($btn){
var verify = verifyDialogSubmit(
        [
          {id: '#jsChangeEmail', tips: Dml.Msg.epMail, errorTips: Dml.Msg.erMail, regName: 'email', require: true},
        ]
    );
    if(!verify){
       return;
    }
    $.ajax({
        cache: false,
        type: 'post',
        dataType:'json',
        url:"/users/update_email/",
        data:$('#jsChangeEmailForm').serialize(),
        async: true,
        beforeSend:function(XMLHttpRequest){
            $btn.val("发送中...");
            $btn.attr('disabled',true);
            $("#jsChangeEmailTips").html("验证中...").show(500);
        },
        success: function(data) {
            if(data.email){
                Dml.fun.showValidateError($('#jsChangeEmail'), data.email);
            }else if(data.status == "success"){
                Dml.fun.showErrorTips($('#jsChangePhoneTips'), "邮箱信息更新成功");
                setTimeout(function(){location.reload();},1000);
            }else{
                 Dml.fun.showValidateError($('#jsChangeEmail'), "邮箱信息更新失败");
            }
        },
        complete: function(XMLHttpRequest){
            $btn.val("完成");
            $btn.removeAttr("disabled");
        }
    });
}

 5.3.2我的课程

Django实战(一)——教育网站_第20张图片

url.py

urlpatterns = [
    # 用户中心
    url(r'^info/$', UserinfoView.as_view(), name="user_info"),

    #我的课程页
    url(r'^mycourse/$', MyCourseView.as_view(), name="mycourse"),
]

views.py

#我的课程
class MyCourseView(LoginRequiredMixin, View):

    def get(self, request):
        user_courses = UserCourse.objects.filter(user=request.user)

        return render(request, 'usercenter-mycourse.html', {
            "user_courses": user_courses,
        })

usercenter-mycourse.html

 {% for user_course in user_courses %}
                    

{{ user_course.course.name }}

课时:{{ user_course.course.learn_time }} 学习人数:{{ user_course.course.student }}
{{ user_course.course.course_org.name }} {{ user_course.course.fav_nums }}
{% endfor %}

 5.3.2我的课程 

 

url.py

urlpatterns = [
    # 用户中心
    url(r'^info/$', UserinfoView.as_view(), name="user_info"),
    #我的课程页
    url(r'^mycourse/$', MyCourseView.as_view(), name="mycourse"),
]

 views.py

#我的课程
class MyCourseView(LoginRequiredMixin, View):

    def get(self, request):
        user_courses = UserCourse.objects.filter(user=request.user)

        return render(request, 'usercenter-mycourse.html', {
            "user_courses": user_courses,
        })

 usercenter-mycourses.html

{% for user_course in user_courses %}

{{ user_course.course.name }}

课时:{{ user_course.course.learn_time }} 学习人数:{{ user_course.course.student }}
{{ user_course.course.course_org.name }} {{ user_course.course.fav_nums }}
{% endfor %}

5.3.3我的收藏

Django实战(一)——教育网站_第21张图片

url.py


urlpatterns = [
    #我的收藏页
    #我收藏的课程机构
    url(r'^myfav/org/$', MyFavOrgView.as_view(), name="myfav_org"),
    #我收藏的教师
    url(r'^myfav/teacher/$', MyFavTeacherView.as_view(), name="myfav_teacher"),
    #我收藏的公开课程
    url(r'^myfav/course/$', MyFavCourseView.as_view(), name="myfav_course"),

]

views.py


# 我的收藏
#1.授课机构收藏
class MyFavOrgView(LoginRequiredMixin, View):

    def get(self, request):
        org_list = []
        fav_orgs = UserFavorite.objects.filter(user=request.user, fav_type=2)
        for fav_org in fav_orgs:
            org_id = fav_org.fav_id
            org = CourseOrg.objects.get(id=org_id)
            org_list.append(org)

        return render(request, 'usercenter-fav-org.html', {
            "org_list": org_list,
        })


#2.教师的收藏
class MyFavTeacherView(LoginRequiredMixin, View):

    def get(self, request):
        teacher_list = []
        fav_teachers = UserFavorite.objects.filter(user=request.user, fav_type=3)
        for fav_teacher in fav_teachers:
            teacher_id = fav_teacher.fav_id
            teacher = Teacher.objects.get(id=teacher_id)
            teacher_list.append(teacher)

        return render(request, 'usercenter-fav-teacher.html', {
            "teacher_list": teacher_list,
        })

#3.公开课程的收藏
class MyFavCourseView(LoginRequiredMixin, View):

    def get(self, request):
        course_list = []
        fav_courses = UserFavorite.objects.filter(user=request.user, fav_type=1)
        for fav_course in fav_courses:
            course_id = fav_course.fav_id
            course = Course.objects.get(id=course_id)
            course_list.append(course)

        return render(request, 'usercenter-fav-course.html', {
            "course_list": course_list,
        })

templates的配置

(1)usercenter-fav-teacher.html

{% for teacher in teacher_list %}

{{ teacher.name }}认证教师

  • 工作年限:{{ teacher.work_years }}年
  • 课程数:{{ teacher.get_course_nums }}
  • 工作公司:{{ teacher.work_company }}
  • 公司职位:{{ teacher.work_position }}
{% endfor %}

(2)usercenter-fav-org.html

{% for org in org_list %}
                    

{{ org.name }}

{{ org.address }}
{% endfor %}

(3)usercenter-fav-course.html

{% for fav_course in course_list %}
                        

{{ fav_course.name }}

时长:{{ fav_course.learn_time }} 学习人数:{{ fav_course.student }}
{{ fav_course.course_org.name }}
{% endfor %}

ajax配置,用于取消收藏。

 5.3.4我的消息

Django实战(一)——教育网站_第22张图片

url.py

urlpatterns = [
    #我的消息
    url(r'^mymessage/$', MymessageView.as_view(), name="mymessage"),
]

 

views.py

#4.我的消息
class MymessageView(LoginRequiredMixin, View):

    def get(self, request):
        all_messages = UserMessage.objects.filter(user=request.user.id)

        #用户进入个人消息后清空未读消息的记录
        all_unread_messages = UserMessage.objects.filter(user=request.user.id, has_read=False)
        for unread_messages in all_unread_messages:
            unread_messages.has_read = True
            unread_messages.save()

        # 对个人消息进行分页
        try:
            page = request.GET.get('page', 1)
        except PageNotAnInteger:
            page = 1
        p = Paginator(all_messages, 3, request=request)
        messages = p.page(page)

        return render(request, 'usercenter-message.html', {
            "messages": messages,
        })
#在view.py 里class rigister写入欢迎注册消息
user_message = UserMessage()
user_message.user = user_profile.id
user_message.message = "欢迎注册慕雪在线网站"
user_message.save()

usercenter-message.html

{% for message in messages.object_list %}
{{ message.add_time }}

{{ message.message }}

{% endfor %}

消息通知器 【小喇叭】

注意:消息通知,当进入消息页面时候,应该显示为已读,即小喇叭变为0

在user.model中写入方法unread_nums

def unread_nums(self):
#获取用户未读消息的数量
    from operation.models import UserMessage
    return UserMessage.objects.filter(user=self.id, has_read=False).count()

在usercenter-base.html中设置


    
{{ request.user.unread_nums }}

5.4 配置首页index.html

(1)退出登陆

url.py

 url('^logout/$', LogoutView.as_view(), name="logout"),

views.py

from django.contrib.auth import authenticate, login ,logout #登陆,退出
from django.http import HttpResponse, HttpResponseRedirect #定向和重定向

#退出登陆
class LogoutView(View):

    def get(self, request):
        logout(request)
        from django.core.urlresolvers import reverse
        return HttpResponseRedirect(reverse("index"))

base页面

(2)配置index.html

url.py

#首页
url('^$', IndexView.as_view(), name="index"),

views.py

#首页开发
class IndexView(View):

    def get(self, request):
        #轮播图
        all_banners = Banner.objects.all().order_by('index')
        courses = Course.objects.filter(is_banner=False)[:6]
        banner_courses = Course.objects.filter(is_banner=False)[:3]
        course_orgs = CourseOrg.objects.all()[:15]

        return render(request, "index.html", {
            "all_banners": all_banners,
            "courses": courses,
            "banner_courses": banner_courses,
            "course_orgs": course_orgs,
        })

注意:在用户登陆的细节,一定要更改一下数据返回方式,否则用户登陆过程中数据无法完成上传

说明1:课程

  • 课程分is_banner=False和is_banner=True,两种课程的class属性不一样
  • is_banner=True的class是  class="module1_2 box"
  • is_banner=True的class是  class="module1_3 box",所以这里要class="module1_{{ forloop.counter|add:2 }}

说明2:课程机构

  • 课程机构的class分为class=""和class="five"
  • 这里要做个判断,class="{% if forloop.counter|divisibleby:5 %}five{% endif %}
  • divisibleby过滤器:能不能整除

说明3:lolgin

  • 当登出在login的时候发现刚才的设置都没生效,看不到图片,要改一下login的view
  • 把之前的登录之后用render到‘index.html’改为return HttpResponseRedirect(reverse('index'))

index.html

课程的显示,templates提供自增1的标签

{% for course in courses %}

{{ course.name }}

难度:{{ course.get_degree_display }} 学习人数:{{ course.student }}
{{ course.course_org.name }} {{ course.fav_nums }}
{% endfor %}

课程机构的显示,templates提供整除5的标签

{% for org in course_orgs %}
    
  • {{ org.tag }}

    {{ org.name }}

  • {% endfor %}

    5.4 配置信息错误页面404和500

    Django实战(一)——教育网站_第23张图片

    url.py

    from MxOnline.settings import STATIC_ROOT
    
    urlpatterns = [
    #静态文件
           url(r'^static/(?P.*)$', serve, {"document_root": STATIC_ROOT}),
    ]
    # 全局404页面配置
    handler404 = 'user.views.page_not_found'
    # 全局500页面配置
    handler500 = 'user.views.page_error'

    settings.py

    DEBUG = False
    
    ALLOWED_HOSTS = ['*']
    
    #静态文件
    STATIC_ROOT = os.path.join(BASE_DIR, 'static')

    注意: 

    • 404和500,生成环境汇总,必须设置debug = False
    • 一旦debug改为false,django就不会代管你的静态文件,所以这里要设置一个url处理静态文件

     user/views.py

    #错误信息404 500
    from django.shortcuts import render_to_response
    
    def page_not_found(request):
        # 全局404处理函数
        response = render_to_response('404.html', {})
        response.status_code = 404
        return response
    
    def page_error(request):
        # 全局500处理函数
        response = render_to_response('500.html', {})
        response.status_code = 500
        return response

    404.html(用户输入异常)





    wow~这个页面被外星人抢走了~


    Wow~ this page was the alien took ~

    500.html(系统异常)







    服务器错误,请稍后重新刷新。

    六、xadmin的进阶开发

    1.权限管理

    DEBUG=True 开发模式,可以用xadmin 

    DEBUG=FALSE 应用模式,不可以使用xadmin

    Django实战(一)——教育网站_第24张图片

    2.自定义icon

     xadmin的图标采用的是第三方css样式“font awesome”,我们可以进官网下载最新的样式替代原本的,下载地址:http://www.fontawesome.com.cn/

    下载完后把里面的“css”和“fonts”两个文件夹拷贝到xadmin的源码(路径:xadmin/static/vendor/font-awesome)里面

    使用方法:

    以course为例

    (1)进官网找到图标的样式

    Django实战(一)——教育网站_第25张图片

     (2)course/adminx.py使用

    # 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']
        model_icon = 'fa fa-book'

    Django实战(一)——教育网站_第26张图片

     

    3.默认排序、只读字段和不显示的字段

    课程:

    • 按点击数倒序排序
    • 点击数不能编辑
    • 不显示收藏人数
    # 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']    #过滤 
        model_icon = 'fa fa-book'            #图标
        ordering = ['-click_nums']           #排序
        readonly_fields = ['click_nums']     #只读字段,不能编辑
        exclude = ['fav_nums']               #不显示的字段

    4.inlines添加数据 

     目前在添加课程的时候没法添加章节和课程资源,我们可以用inlines去实现这一功能

    class LessonInline(object):
        model = Lesson
        extra = 0
    
    class CourseResourceInline(object):
        model = CourseResource
        extra = 0
    
    #在CourseAdmin中使用inlines添加上面两个的方法
    class CourseAdmin(object):
        inlines = [LessonInline,CourseResourceInline]    #增加章节和课程资源

    效果如下:

    再添加课程的时候,可以直接添加章节和课程资源

    Django实战(一)——教育网站_第27张图片

     

    5.一张表分两个Model来管理

    课程里面分为轮播课程和不是轮播课程两种类型,我们可以分开来管理

    (1)在course/models.py里面新建一个Model

    class BannerCourse(Course):
        '''显示轮播课程'''
        class Meta:
            verbose_name = '轮播课程'
            verbose_name_plural = verbose_name
            #这里必须设置proxy=True,这样就不会再生成一张表,同时还具有Model的功能
            proxy = True

     

    (2)course/adminx.py

    from .models import BannerCourse
    
    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']    #过滤
        model_icon = 'fa fa-book'            #图标
        ordering = ['-click_nums']           #排序
        readonly_fields = ['click_nums']     #只读字段
        exclude = ['fav_nums']               #不显示的字段
        inlines = [LessonInline,CourseResourceInline]    #增加章节和课程资源
    
        def queryset(self):
            # 重载queryset方法,来过滤出我们想要的数据的
            qs = super(CourseAdmin, self).queryset()
            # 只显示is_banner=True的课程
            qs = qs.filter(is_banner=False)
            return qs
    
    
    class BannerCourseAdmin(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']
        model_icon = 'fa fa-book'
        ordering = ['-click_nums']
        readonly_fields = ['click_nums']
        exclude = ['fav_nums']
        inlines = [LessonInline,CourseResourceInline]
    
        def queryset(self):
            #重载queryset方法,来过滤出我们想要的数据的
            qs = super(BannerCourseAdmin, self).queryset()
            #只显示is_banner=True的课程
            qs = qs.filter(is_banner=True)
            return qs
    
    # 将管理器与model进行注册关联
    xadmin.site.register(Course, CourseAdmin)
    xadmin.site.register(BannerCourse, BannerCourseAdmin)

    后台:可以看到多了一个轮播课程,来达到分类管理的一个功能

    Django实战(一)——教育网站_第28张图片

     Django实战(一)——教育网站_第29张图片

     

    6.xadmin的其它常见功能

    (1)list_editable

    在列表页可以直接编辑的

    class CourseAdmin(object):
        list_editable = ['degree','desc']

    Django实战(一)——教育网站_第30张图片

     (2)自定义函数作为列显示

    class Course(models.Model):
    
        def get_zj_nums(self):
            #获取课程的章节数
            return self.lesson_set.all().count()
        get_zj_nums.short_description = '章节数'   #在后台显示的名称

    course/adminx.py中

    class CourseAdmin(object):
        list_display = ['get_zj_nums']  #直接使用函数名作为字段显示

    效果:列表字段多了个“章节数”

    Django实战(一)——教育网站_第31张图片

     

    (3)显示自定义的html代码

    course/models.py中

    class Course(models.Model):
    
        def go_to(self):
            from django.utils.safestring import mark_safe
            #mark_safe后就不会转义
            return mark_safe("跳转")
        go_to.short_description = "跳转"
    

    course/adminx.py中

    class CourseAdmin(object):
        list_display = ['go_to']

    效果:多了一个列表“跳转”,点击后跳转到上面定义的地址

    Django实战(一)——教育网站_第32张图片

     

    (4)refresh定时刷新工具

     course/adminx.py中

    class CourseAdmin(object):
        refresh_times = [3,5] #自动刷新(里面是秒数)

    后台效果:

    可以选择3s或者5s自动刷新页面

     

    Django实战(一)——教育网站_第33张图片

    (5)字段联动

     应用场景:当添加一门课程的时候,希望课程机构里面的课程数 +1

     重写xadmin的save_models方法

     

    class CourseAdmin(object):
        
        def save_models(self):
            # 在保存课程的时候统计课程机构的课程数
            # obj实际是一个course对象
            obj = self.new_obj
            # 如果这里不保存,新增课程,统计的课程数会少一个
            obj.save()
            # 确定课程的课程机构存在。
            if obj.course_org is not None:
                #找到添加的课程的课程机构
                course_org = obj.course_org
                #课程机构的课程数量等于添加课程后的数量
                course_org.course_nums = Course.objects.filter(course_org=course_org).count()
                course_org.save()

     

     

     course/adminx.py全部代码

     

    7.增加富文本编辑器Ueditor

     

    Django实战(一)——教育网站_第34张图片

     

    (1)下载

    地址:https://github.com/twz915/DjangoUeditor3/

    解压后,把DjangoUeditor文件夹拷贝到项目目录下面

    注意:直接pip install DjangoUeditor的方法会出问题

    Django实战(一)——教育网站_第35张图片

    (2)settings中添加app

    INSTALLED_APPS = [
        'DjangoUeditor',
    ]

    (3)MxOnline/urls.py

      
     # 富文本编辑器url
        path('ueditor/',include('DjangoUeditor.urls' )),

     (4)course/models.py中Course修改detail字段

    class Course(models.Model):
        # detail = models.TextField("课程详情")
        detail = UEditorField(verbose_name=u'课程详情', width=600, height=300, imagePath="courses/ueditor/",
                              filePath="courses/ueditor/", default='')

    (5)xadmin/plugs目录下新建ueditor.py文件,代码如下

     

    import xadmin
    from xadmin.views import BaseAdminPlugin, CreateAdminView, ModelFormAdminView, UpdateAdminView
    from DjangoUeditor.models import UEditorField
    from DjangoUeditor.widgets import UEditorWidget
    from django.conf import settings
    
    
    class XadminUEditorWidget(UEditorWidget):
        def __init__(self, **kwargs):
            self.ueditor_options = kwargs
            self.Media.js = None
            super(XadminUEditorWidget,self).__init__(kwargs)
    
    
    class UeditorPlugin(BaseAdminPlugin):
    
        def get_field_style(self, attrs, db_field, style, **kwargs):
            if style == 'ueditor':
                if isinstance(db_field, UEditorField):
                    widget = db_field.formfield().widget
                    param = {}
                    param.update(widget.ueditor_settings)
                    param.update(widget.attrs)
                    return {'widget':XadminUEditorWidget(**param)}
            return attrs
    
        def block_extrahead(self, context, nodes):
            js  = '' %(settings.STATIC_URL + "ueditor/ueditor.config.js")
            js += '' %(settings.STATIC_URL + "ueditor/ueditor.all.min.js")
            nodes.append(js)
    
    xadmin.site.register_plugin(UeditorPlugin, UpdateAdminView)
    xadmin.site.register_plugin(UeditorPlugin, CreateAdminView)

     

     

    (6)xadmin/plugs/__init__.py里面添加ueditor插件

    PLUGINS = (
       'ueditor',
    )

    (7)course/adminx.py中使用

    class CourseAdmin(object):
        #detail就是要显示为富文本的字段名
        style_fields = {"detail": "ueditor"}

    (8)course-detail.html

    在模板中必须关闭Django的自动转义才能正常显示

    {% autoescape off %} {{ course.detail }} {% endautoescape %}

    最终效果:

    后台编辑页面

    Django实战(一)——教育网站_第36张图片

     

    【剧终】 

     

    你可能感兴趣的:(dijango)