Django开发在线教育平台--学习整理(一)

作者:SimonDM,转行求职中

写作目的:无他,但求手熟尔

一、前言

  • 开发环境:
    • Python 3.6.4
    • Pycharm 2017.3.3
    • PostgreSQL 10.3
    • pgAdmin4
    • Django 2.0.4
  • git仓库地址:https://github.com/SimonMQ/MxOnline2
  • 题外话:写作真的是很麻烦的一件事,要截图,还要粘代码。不过跟看着教程写东西比起来思路倒是清晰了一些,边学边练。勉之!

二、需求分析

  • 项目介绍
    • 系统具有完整的用户登录注册以及找回密码功能,拥有完整个人中心。
    • 个人中心: 修改头像,修改密码,修改邮箱,可以看到我的课程以及我的收藏。可以删除收藏,我的消息。
    • 导航栏: 公开课,授课讲师,授课机构,全局搜索。
    • 点击公开课–> 课程列表,排序-搜索。热门课程推荐,课程的分页。
    • 点击课程–> 课程详情页中对课程进行收藏,取消收藏。富文本展示课程内容。
    • 点击开始学习–> 课程的章节信息,课程的评论信息。课程资源的下载链接。
    • 点击授课讲师–>授课讲师列表页,对讲师进行人气排序以及分页,右边有讲师排行榜。
    • 点击讲师的详情页面–> 对讲师进行收藏和分享,以及讲师的全部课程。
    • 导航栏: 授课机构有分页,排序筛选功能。
    • 机构列表页右侧有快速提交我要学习的表单。
    • 点击机构–> 左侧:机构首页,机构课程,机构介绍,机构讲师。
    • 后台管理系统可以切换主题。左侧每一个功能都有列表显示, 增删改查,筛选功能。
    • 课程列表页可以对不同字段进行排序。选择多条记录进行删除操作。
    • 课程列表页:过滤器->选择字段范围等,搜索,导出csv,xml,json。
    • 课程新增页面上传图片,富文本的编辑。时间选择,添加章节,添加课程资源。
    • 日志记录:记录后台人员的操作
  • 系统功能
copy过来的,应该是视频教程里的图
  • 模型设计
自己画的思维导图
  • 模块设计

三、初始化环境

1、新建工程

首先,打开cmd,cd到存放django项目的文件夹,创建一个新工程(也可以用虚拟环境virtualenv):

django-admin startproject MxOnline2

2、创建app

创建好工程之后就是配置整个工程的目录结构,先创建四个app:

python manage.py startapp users
python manage.py startapp course
python manage.py startapp organization
python manage.py startapp operation

然后,在同级目录下创建一个package,名为apps,用于存放上述四个app。将四个app剪切到apps中,右击apps -> Mark Directory as -> Sources Root。当django在根目录下找不到app时会去apps中去寻找,但此时pycharm知道这么做,而django不知道,所以还要到settings中配置。

import os
import sys

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
# 将四个app放到一个apps包中之后,由于找不到路径,配置此项。插入第0是希望先搜索apps目录下的文件
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))

之后,在INSTALLED_APPS中注册四个app

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'users',
    'course',
    'organization',
    'operation',
]

3、配置目录结构

新建static文件夹用于存放静态文件,css/js/img等;

新建templates文件夹用于存放html文件;

新建media文件夹用于存放后台上传的图片、视频等文件。

配置settings

static、templates和media虽然创建好了,但是django还无法找到,所以必须在settings中配置。

注意:

STATIC_URL的作用是映射静态文件的url,只在templates中引用的时候用到,其用法与MEDIA_URL相同。

STATICFILES_DIRS的作用是由于我们在app之外设置了其它的static目录。由于django在运行某个app的html时会默认查找这个app下的’static‘目录,所以在app之外的static需要我们自己配置。这个用法在DEBUG=TRUE时生效,FALSE时django则不会代管静态文件,所以,在部署时会用到STATIC_ROOT。STATICFILES_DIRS的用法与TEMPLATES_DIRS相同。

# 配置静态文件路径
STATIC_URL = '/static/'
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)
# 配置templates路径
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
# 配置上传的媒体路径
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

4、其它配置:

语言和时区

LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

5、数据库配置

# 配置数据库库,使用PostgreSQL
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mxonline2',
        'USER': 'postgres',
        'PASSWORD': 'MAQING',
        'HOST': '127.0.0.1',
        'PORT': '5432',
    }
}

打开pgAdmin4,新建数据库mxonline2。

四、模型设计Model

整个项目配置完之后,就要开始着手设计Model。

  • organization
from django.db import models
from datetime import datetime


# Create your models here.
'''
points:
1、verbose_name的作用是给予对象一个人类可读的名字,”A human-readable name for the object“。
用于table中某个字段时,在admin后台会显示verbose_name,用于Meta中时,显示的是当前数据表的名称。
2、max_length为最大字符长度,由于CharField在数据库中对应为varchar,最大长度为255,
所以这里设置的最大值也不能超过255,否则,用TextField代替。max_length的计算方法:len("字符串")=3。
3、ImageField继承自FileField,用于上传文件,其中的upload_to属性,用于指定上传文件的目录,该目录会在MEDIA_ROOT下自动生成。
如使用upload_to='uploads/%Y/%m/%d/',文件会上传到MEDIA_ROOT/uploads/2015/01/30中,
/%Y/%m/%d/为strftime()格式化的xxxx年xx月xx日。
4、null=True和blank=True通常一起使用,null代表数据库可以为空,blank代表后台表单数据填写时可以留白。
5、choices用于选择框,在使用前应该在class中定义一个可迭代对象,[(A, B), (A, B) ...],每个元组中第一个
元素代表实际值,第二个是人类可读名称,类似于verbose_name。
'''


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

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

    def __str__(self):
        return self.name


class CourseOrg(models.Model):
    ORG_CHOICES = (
        ('pxjg', '培训机构'),
        ('gx', '高校'),
        ('gr', '个人'),
    )

    name = models.CharField(verbose_name="机构名称", max_length=20)
    desc = models.TextField(verbose_name="机构描述")
    category = models.CharField(verbose_name="机构类别", max_length=20, choices=ORG_CHOICES, default='pxjg')
    tag = models.CharField(verbose_name="机构标签", max_length=20, default="全国知名")
    image = models.ImageField(verbose_name="封面图", upload_to='org/%Y/%m')
    address = models.CharField(verbose_name="机构地址", max_length=200)
    city = models.ForeignKey(City, verbose_name="所在城市", on_delete=models.CASCADE)
    click_nums = models.IntegerField(verbose_name="点击数", default=0)
    learn_nums = models.IntegerField(verbose_name="学习人数", default=0)
    fav_nums = models.IntegerField(verbose_name="收藏数", default=0)
    course_nums = models.IntegerField(verbose_name="课程数", default=0)
    add_time = models.DateTimeField(verbose_name="添加时间", default=datetime.now)

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

    def __str__(self):
        return self.name


class Teacher(models.Model):
    org = models.ForeignKey(CourseOrg, verbose_name="所属机构", on_delete=models.CASCADE)
    name = models.CharField(verbose_name="姓名", max_length=20)
    age = models.IntegerField(verbose_name="年龄", default=0)
    image = models.ImageField(verbose_name="头像", upload_to='teacher/%Y/%m', default='', null=True, blank=True)
    work_year = models.IntegerField(verbose_name="工作年限", default=0)
    work_company = models.CharField(verbose_name="就职公司", max_length=50)
    work_position = models.CharField(verbose_name="工作岗位", max_length=50)
    points = models.CharField(verbose_name="教学特点", max_length=50)
    click_nums = models.IntegerField(verbose_name="点击数", default=0)
    fav_nums = models.IntegerField(verbose_name="收藏数", default=0)
    add_time = models.DateTimeField(verbose_name="添加时间", default=datetime.now)

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

    def __str__(self):
        return self.name
  • course
from django.db import models
from datetime import datetime
from organization.models import CourseOrg, Teacher


# Create your models here.
class Course(models.Model):
    DEGREE_CHOICES = (
        ('cj', '初级'),
        ('zj', '中级'),
        ('gj', '高级'),
    )
    course_org = models.ForeignKey(CourseOrg, verbose_name="所属机构", on_delete=models.CASCADE)
    teacher = models.ForeignKey(Teacher, verbose_name="授课教师", on_delete=models.CASCADE)
    name = models.CharField(verbose_name="名称", max_length=20)
    desc = models.CharField(verbose_name="课程描述", max_length=255)
    detail = models.TextField(verbose_name="课程详情")
    image = models.ImageField(verbose_name="封面图", upload_to='course/%Y/%m')
    degree = models.CharField(verbose_name="难度", choices=DEGREE_CHOICES, max_length=2, default='cj')
    is_banner = models.BooleanField(verbose_name="是否轮播", default=False)
    learn_times = models.IntegerField(verbose_name="课程时长(分钟数)", default=0)
    click_nums = models.IntegerField(verbose_name="点击数", default=0)
    learn_nums = models.IntegerField(verbose_name="学习人数", default=0)
    fav_nums = models.IntegerField(verbose_name="收藏数", default=0)
    category = models.CharField(verbose_name="分类", max_length=20)
    tag = models.CharField(verbose_name="标签", max_length=20)
    you_need_know = models.CharField(verbose_name="课程须知", max_length=255, default="本课程需要静心阅读")
    teacher_tell = models.CharField(verbose_name="老师告诉你", max_length=255, default="好好学习,天天向上")
    add_time = models.DateTimeField(verbose_name="添加时间", default=datetime.now)

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

    def __str__(self):
        return self.name


class Lesson(models.Model):
    course = models.ForeignKey(Course, verbose_name="所属课程", on_delete=models.CASCADE)
    name = models.CharField(verbose_name="章节名", max_length=20)
    add_time = models.DateTimeField(verbose_name="添加时间", default=datetime.now)

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

    def __str__(self):
        return '《{0}》课程的章节 >> {1}'.format(self.course, self.name)


class Video(models.Model):
    lesson = models.ForeignKey(Lesson, verbose_name="所属章节", on_delete=models.CASCADE)
    name = models.CharField(verbose_name="视频名", max_length=20)
    # 这里的视频地址是否可以替换为一个FileField?
    url = models.URLField(verbose_name="视频地址", max_length=200, default='')
    learn_times = models.IntegerField(verbose_name="视频时长(分钟数)", default=0)
    add_time = models.DateTimeField(verbose_name="添加时间", default=datetime.now)

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

    def __str__(self):
        return '《{0}》章节的视频 >> {1}'.format(self.lesson, self.name)


class CourseResource(models.Model):
    course = models.ForeignKey(Course, verbose_name="所属课程", on_delete=models.CASCADE)
    name = models.CharField(verbose_name="资源名", max_length=20)
    resource = models.FileField(verbose_name="资源文件", upload_to='course/resource/%Y%m')
    add_time = models.DateTimeField(verbose_name="添加时间", default=datetime.now)

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

    def __str__(self):
        return self.name
  • users
from django.db import models
from datetime import datetime

from django.contrib.auth.models import AbstractUser


# Create your models here.
class UserProfile(AbstractUser):
    GENDER_CHOICES = (
        ('male', '男'),
        ('female', '女'),
    )
    nick_name = models.CharField(verbose_name="昵称", max_length=20)
    birthday = models.DateField(verbose_name="生日", null=True, blank=True)
    gender = models.CharField(verbose_name="性别", choices=GENDER_CHOICES, max_length=6, default='male')
    address = models.CharField(verbose_name="地址", max_length=100, default='')
    mobile = models.CharField(verbose_name="手机号", max_length=11, null=True, blank=True)
    image = models.ImageField(verbose_name="头像", upload_to='image/%Y/%m', default='')

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

    def __str__(self):
        return self.username


class EmailVerifyRecord(models.Model):
    VERIFY_RECORD_CHOICES = (
        ('register', '注册'),
        ('forget', '找回密码'),
        ('update_email', '修改邮箱'),
    )
    code = models.CharField(verbose_name="验证码", max_length=20)
    email = models.EmailField(verbose_name="邮箱", max_length=100)
    send_type = models.CharField(verbose_name="验证码类型", choices=VERIFY_RECORD_CHOICES, max_length=20)
    send_time = models.DateTimeField(verbose_name="", default=datetime.now)

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

    def __str__(self):
        return '{0}({1})'.format(self.code, self.email)


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

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

    def __str__(self):
        return '{0}(位于第{1}位)'.format(self.title, self.index)
  • operation
from django.db import models
from datetime import datetime
from course.models import Course
from users.models import UserProfile


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

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

    def __str__(self):
        return '用户:{0} 的手机号:{1}'.format(self.name, self.mobile)


class CourseComment(models.Model):
    course = models.ForeignKey(Course, verbose_name="评论课程", on_delete=models.CASCADE)
    user = models.ForeignKey(UserProfile, verbose_name="评论用户", on_delete=models.CASCADE)
    comment = models.CharField(verbose_name="评论内容", max_length=255)
    add_time = models.DateTimeField(verbose_name="添加时间", default=datetime.now)

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

    def __str__(self):
        return '用户({0})对于《{1}》的评论'.format(self.user, self.course)


class UserFavorite(models.Model):
    TYPE_CHOICES = (
        (1, '机构'),
        (2, '课程'),
        (3, '教师'),
    )
    user = models.ForeignKey(UserProfile, verbose_name="用户", on_delete=models.CASCADE)
    fav_type = models.IntegerField(verbose_name="收藏类型", choices=TYPE_CHOICES, default=1)
    add_time = models.DateTimeField(verbose_name="添加时间", default=datetime.now)

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

    def __str__(self):
        return '用户({0})收藏了{1}'.format(self.user, self.fav_type)


class UserMessage(models.Model):
    # 为0则发送给所有用户,否则就是用户的id
    user = models.IntegerField(verbose_name="用户", default=0)
    message = models.CharField(verbose_name="消息内容", max_length=255)
    has_read = models.BooleanField(verbose_name="是否已读", default=False)
    add_time = models.DateTimeField(verbose_name="添加时间", default=datetime.now)

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

    def __str__(self):
        return '用户({0})接收了消息:{1}'.format(self.user, self.message)


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

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

    def __str__(self):
        return '用户({0})学习了《{1}》'.format(self.user, self.course)

这里需要注意的是,django为我们提供了User表,功能包括:id、password、is_superuser、is_active、email等。如果我们需要扩展可以复写AbsractUser模型,之后,需要在settings中告诉django我们新的用户模型:

# 由于复写了user模型,我们需要重载AUTH_USER_MODEL参数,导入我们复写后的模型
AUTH_USER_MODEL = 'users.UserProfile'

然后运行migrate,将写好的数据迁移到数据库中:

python manage.py makemigrations
python manage.py migrate

这时,打开pgAdmin4,就会发现创建了24张table,其中除了我们刚刚创建的15张之外,还有系统自动生成的,包括迁移记录,会话等等。

另外,右击某张表,选择View/All Rows可以查看当前表的字段和数据信息。

五、Admin后台管理系统

使用django自带的Admin之前先创建一个超级管理员账户:

E:\DjangoProjects\MxOnline2>python manage.py createsuperuse
Username: simondm
Email address: 352830021@qq.com
Password:
Password (again):
Superuser created successfully.

然后,在每个app下的admin.py中注册并定制admin后台模型:

from django.contrib import admin
from .models import Course, Lesson, Video, CourseResource


# Register your models here.
# 在admin后台注册模型,并且定制后台
class CourseAdmin(admin.ModelAdmin):
    # 设置fieldsets 控制管理“添加”和 “更改” 页面的布局,顺便可以给这些字段排序
    fieldsets = (
        (None, {
            'fields': ('name', 'desc', 'tag', 'is_banner', ('course_org', 'teacher'), 'degree', 'learn_times', ('click_nums', 'learn_nums', 'fav_nums'), 'category')
        }),
        ('其它选项', {
            'fields': ('detail', 'image', 'you_need_know', 'teacher_tell', 'add_time')
        }),
    )

    # 指定修改页面上显示的字段,如果不指定,则只显示__str__()指定的那一列。
    list_display = ('name', 'desc', 'course_org', 'teacher', 'is_banner', 'colored_degree', 'learn_times', 'learn_nums')
    search_fields = ('name', 'desc', 'detail', 'degree', 'learn_nums')
    list_filter = ('name', 'desc', 'detail', 'degree', 'learn_times', 'learn_nums')


class LessonAdmin(admin.ModelAdmin):
    list_display = ('course', 'name', 'add_time')
    search_fields = ('course', 'name')
    # 由于course是一个外键,所以过滤的时候根据课程名称过滤,即course.name
    list_filter = ('course__name', 'name', 'add_time')


class VideoAdmin(admin.ModelAdmin):
    list_display = ('lesson', 'name', 'add_time')
    search_fields = ('lesson', 'name')
    list_filter = ('lesson', 'name', 'add_time')


class CourseResourceAdmin(admin.ModelAdmin):
    list_display = ('course', 'name', 'resource', 'add_time')
    search_fields = ('course', 'name', 'resource')
    list_filter = ('course__name', 'name', 'resource', 'add_time')


# models与admin关联注册
admin.site.register(Course, CourseAdmin)
admin.site.register(Lesson, LessonAdmin)
admin.site.register(Video, VideoAdmin)
admin.site.register(CourseResource, CourseResourceAdmin)

常用几个功能:

list_display:设置修改页面显示哪些字段

search_fields:设置搜索框搜索数据

list_filter:设置右侧筛选栏

fieldsets:设置页面布局

设置好后的页面:

后台首页:

课程Table页:

修改课程详情页:

这里只是实现简单的功能,django的admin功能非常强大,待后续完善。

基本的结构搭建好了,模型有了,admin也能用了,下面就开始把前端页面构建起来了。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏小巫技术博客

应用被强杀了怎么办

11120
来自专栏程序猿DD

极大提高工作效率的几十种神兵利器

来源:https://www.liutf.com/posts/3720794851.html

24830
来自专栏androidBlog

360面试总结(Android)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/...

9910
来自专栏哲学驱动设计

产品前端重构(TypeScript、MVC框架设计)

最近两周完成了对公司某一产品的前端重构,本文记录重构的主要思路及相关的设计内容。 公司期望把某一管理类信息系统从项目代码中抽取、重构为一个可复用的产品。该系统的...

28680
来自专栏小樱的经验随笔

深入理解USB流量数据包的抓取与分析

在一次演练中,我们通过wireshark抓取了一个如下的数据包,我们如何对其进行分析?

42420
来自专栏coding

听说,撸代码,ide与vim更配哦vim折腾记vim常用命令

在选择编辑器上面,我是一个纠结的人,曾经年少的我执着地追求一款万能的编辑器,可以支持所有编辑语言,灵活可定制,可纯粹用键盘操作。符合这种条件的编辑器,非vim莫...

11020
来自专栏数据小魔方

数据地图系列6|Stata数据地图(下)

今天要跟大家分享的是数据地图系列6——Stata数据地图(下)! 接着前一篇的节凑,这一篇会给大家介绍比较全面的Stata热力地图代码实现。 版本仍然是基于S...

73240
来自专栏康怀帅的专栏

Ubuntu 常用软件

本文列举了 Ubuntu 常用软件。 娱乐 网易云音乐 http://music.163.com/#/download mpv 播放器 https://mpv....

53650
来自专栏杨建荣的学习笔记

system表空间不足的问题分析(r6笔记第66天)

很多事情见多了也就有了麻木的感觉,报警短信就是如此,每天总能收到不少的报警短信,可能很多时候就扫一眼,如果没有严重的问题自己是不会情愿打开电脑处理的。 对于此,...

28840
来自专栏测试开发架构之路

Android软件测试Monkey测试工具

前言: 最近开始研究Android自动化测试方法,对其中的一些工具、方法和框架做了一些简单的整理,其中包括android测试框架、CTS、Monkey、Monk...

1.1K110

扫码关注云+社区

领取腾讯云代金券