基于Django开发的免费在线选修课程推荐系统源码获取方法?
- 内容介绍
- 文章标签
- 相关推荐
本文共计3502个文字,预计阅读时间需要15分钟。
一、搭建项目+Python解释器版本使用3.7.8。
二、创建虚拟环境+在D盘下创建文件夹my_work,然后在其中创建两个文件夹:pro和venv。
三、Win+R输入cmd进入文件夹venv,执行以下命令创建虚拟环境:
bashpython -m venv venv一、搭建项目
python解释器版本使用3.7.8。
1、创建虚拟环境
在d盘下创建一个文件夹my_work,然后在里面创建两个文件夹:pro和venv。
win+R输入cmd进入文件夹venv,然后执行以下命令创建虚拟环境:
python -m venv course_manager激活虚拟环境:
cd course_manager cd Scripts activate导入django:
pip install Django==3.0.7 -i pypi.mirrors.ustc.edu.cn/simple/ pip install PyMySQL==0.9.2 -i pypi.mirrors.ustc.edu.cn/simple/ pip install xadmin-django==3.0.2 -i pypi.mirrors.ustc.edu.cn/simple/ pip install mysqlclient==2.0.1 -i pypi.mirrors.ustc.edu.cn/simple/ pip install numpy==1.21.6 -i pypi.mirrors.ustc.edu.cn/simple/然后进入pro目录
cd .. cd .. cd .. cd pro2、创建项目
执行命令:
django-admin startproject course_manager3、创建子应用
切换到项目根目录:
cd course_manager创建子应用:
python manage.py startapp course自此项目创建完成。
二、settings.py配置
1、创建数据库
使用MySQL可视化工具创建一个数据库course_manager。
2、PyCharm打开项目
使用PyCharm打开项目:file->open.
在项目根目录下创建以下文件夹:
static、media、imgs、log、templates。
其中media中再创建一个文件夹course_cover存放课程封面。
选中templates->右键->Make Directory as- >Template Folder.
3、配置项目虚拟环境
4、允许所有网站访问
在course_manager\settings.py中做修改:
ALLOWED_HOSTS = ['*']5、添加子应用
在course_manager\settings.py中的INSTALLED_APPS加入子应用course,如下:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'xadmin', 'crispy_forms', 'course' ]6、添加templates目录
在course_manager\settings.py中的TEMPLATES中DIRS改为:
'DIRS': [os.path.join(BASE_DIR, 'templates')],7、使用mysql数据库
把在course_manager\settings.py中的DATABASES注释掉,改为:
ip = '127.0.0.1' DATABASE_NAME = 'course_manager' # mysql数据库名称 DATABASE_USER = 'root' # mysql数据库用户名 DATABASE_PASS = 'xxx' # mysql数据库密码 DATABASE_HOST = ip # mysql数据库IP DATABASE_PORT = 3306 # mysql数据库端口 # 配置数据库 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # 修改数据库为MySQL,并进行配置 'NAME': DATABASE_NAME, # 'USER': DATABASE_USER, # 用户名 'PASSWORD': DATABASE_PASS, # 密码 'HOST': DATABASE_HOST, 'PORT': DATABASE_PORT, 'OPTIONS': {'charset': 'utf8mb4', } } }8、使用中文
把course_manager\settings.py的LANGUAGE_CODE、TIME_ZONE和USE_TZ改为:
LANGUAGE_CODE = 'zh-hans' # 使用中国时区 TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_L10N = True USE_TZ = False9、配置静态文件路由
把course_manager\settings.py的STATIC_URL改为:
STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ] # STATIC_ROOT = os.path.join(BASE_DIR, 'static') # 收集静态文件时打开,然后关闭STATICFILES_DIRS MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_USER_ICON = os.path.join(BASE_DIR, 'media/user_icon')三、models.py数据表
在course\models.py中创建用户表、课程类别表、课程表、用户选课表、评分表、收藏表、评论表:
from django.db import models # 用户表 class User(models.Model): username = models.CharField(max_length=255, unique=True, verbose_name="账号") password = models.CharField(default='123456', max_length=32, verbose_name="密码") class Meta: db_table = 'user' verbose_name_plural = "用户" verbose_name = "用户" def __str__(self): return self.username # 课程类别表 class Tags(models.Model): name = models.CharField(max_length=200, verbose_name="类别") class Meta: db_table = 'tags' verbose_name = "课程类别" verbose_name_plural = "课程类别" def __str__(self): return self.name # 课程表 class CourseInfo(models.Model): name = models.CharField(verbose_name="课程名", max_length=255) course_code = models.CharField(unique=True, verbose_name="课程编号", max_length=255) teacher = models.CharField(verbose_name="讲师", max_length=255) about = models.TextField(verbose_name="描述") pic = models.FileField(verbose_name="封面图片", max_length=64, upload_to='course_cover') tags = models.ForeignKey( Tags, on_delete=models.CASCADE, verbose_name="类别", related_name="tags", blank=True, null=True, ) select_num = models.IntegerField(verbose_name="选修人数", default=0) look_num = models.IntegerField(verbose_name="浏览人数", default=0) collect_num = models.IntegerField(verbose_name="收藏人数", default=0) course_url = models.TextField(verbose_name="课程网址", default='www.xuetangx.com/') add_time = models.DateTimeField(verbose_name="创建时间", auto_now_add=True) class Meta: db_table = 'course_info' verbose_name = "课程" verbose_name_plural = "课程" def __str__(self): return self.name # 用户选课表 class UserCourse(models.Model): course = models.ForeignKey( CourseInfo, to_field='course_code', on_delete=models.CASCADE, blank=True, null=True, verbose_name="课程id" ) user = models.ForeignKey( User, on_delete=models.CASCADE, blank=True, null=True, verbose_name="用户id", ) enroll_time = models.DateTimeField(verbose_name="选修时间", auto_now_add=True) class Meta: db_table = 'user_course' verbose_name = "用户选课信息" verbose_name_plural = verbose_name # 评分表 class RateCourse(models.Model): course = models.ForeignKey( CourseInfo, to_field='course_code', on_delete=models.CASCADE, related_name='rate_course', blank=True, null=True, verbose_name="课程id" ) user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True, verbose_name="用户id") mark = models.FloatField(verbose_name="评分") create_time = models.DateTimeField(verbose_name="添加时间", auto_now_add=True) class Meta: db_table = 'rate_course' verbose_name = "评分信息" verbose_name_plural = verbose_name # 收藏表 class CollectCourse(models.Model): course = models.ForeignKey( CourseInfo, to_field='course_code', on_delete=models.CASCADE, related_name='collect_course', blank=True, null=True, verbose_name="课程id" ) user = models.ForeignKey( User, on_delete=models.CASCADE, blank=True, null=True, verbose_name="用户id", ) is_delete = models.BooleanField(default=False, verbose_name='是否取消') create_time = models.DateTimeField(verbose_name="收藏时间", auto_now_add=True) class Meta: db_table = 'collect_course' verbose_name = "课程收藏" verbose_name_plural = verbose_name # 评论表 class CommentCourse(models.Model): course = models.ForeignKey( CourseInfo, to_field='course_code', on_delete=models.CASCADE, related_name='comment_course', blank=True, null=True, verbose_name="课程id" ) user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True, verbose_name="用户") content = models.TextField(verbose_name="评论内容") create_time = models.DateTimeField(verbose_name='评论时间', auto_now_add=True) like_num = models.IntegerField(verbose_name="点赞数", default=0) like_users = models.TextField(null=True, blank=True, default=None, verbose_name="点赞用户") is_show = models.BooleanField(default=True, verbose_name='是否显示') class Meta: db_table = 'comment_course' verbose_name = "课程评论" verbose_name_plural = verbose_name四、urls.py路由配置
1、修改course_manager\urls.py
在course下创建一个urls.py,并让course_manager\urls.py分配路由,
其中course_manager\urls.py改为:
import xadmin from django.urls import path, re_path, include from django.views.generic import RedirectView from django.views.static import serve from django.conf import settings urlpatterns = [ path('xadmin/', xadmin.site.urls), path("", include("course.urls")), # favicon.cio re_path(r'^favicon\.ico$', RedirectView.as_view(url=r'media/favicon.ico')), re_path(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}), # re_path(r'^static/(?P<path>.*)$', serve, {'document_root': settings.STATICFILES_DIRS}), # 收集静态文件时关闭 path(r'^static/(?P<path>.*)$', serve, {'document_root': settings.STATIC_ROOT}), # 收集静态文件时打开,然后关闭STATICFILES_DIRS ]2、修改course\urls.py
先改为:
from django.urls import path, re_path from course import views urlpatterns = [ ]后面会添加各种路由。
3、数据迁移
在pycharm左下角的Terminal里执行数据迁移命令
python manage.py makemigrations python manage.py migrate4、创建缓存表
python manage.py createcachetable5、收集静态文件
先把course_manager\settings.py中的静态文件路由改为:
STATIC_URL = '/static/' # STATICFILES_DIRS = [ # os.path.join(BASE_DIR, 'static'), # ] STATIC_ROOT = os.path.join(BASE_DIR, 'static') # 收集静态文件时打开,然后关闭STATICFILES_DIRS然后执行:
python manage.py collectstatic执行成功后,把course_manager\settings.py中的静态文件路由改为:
STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ] # STATIC_ROOT = os.path.join(BASE_DIR, 'static') # 收集静态文件时打开,然后关闭STATICFILES_DIRS把course_manager\urls.py改为:
import xadmin from django.urls import path, re_path, include from django.views.generic import RedirectView from django.views.static import serve from django.conf import settings urlpatterns = [ path('xadmin/', xadmin.site.urls), path("", include("course.urls")), # favicon.cio re_path(r'^favicon\.ico$', RedirectView.as_view(url=r'media/favicon.ico')), re_path(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}), re_path(r'^static/(?P<path>.*)$', serve, {'document_root': settings.STATICFILES_DIRS}), # 收集静态文件时关闭 # path(r'^static/(?P<path>.*)$', serve, {'document_root': settings.STATIC_ROOT}), # 收集静态文件时打开,然后关闭STATICFILES_DIRS ]6、创建后台管理员
python manage.py createsuperuser 设置账号为 root 邮箱为 1@qq.com 密码为 course-root五、导入基础数据
把根目录下的course.sql在mysql可视化工具中执行即可。
六、核心代码
1、static创建文件夹
在static目录下创建三个文件夹image、css、js和fonts用来存放前端需要使用到的文件。
2、base.html前端框架
在目录templates下创建前端页面框架base.html,代码如下:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta fontawesome.dashgame.com/ 图标字体网站 # 基础设置 class BaseSetting(object): enable_themes = True # 使用主题 use_bootswatch = True # 全局设置 class GlobalSettings(object): site_title = '课程管理系统' # 标题 site_footer = mark_safe('课程推荐系统') # 页尾 site_url = '/' menu_style = 'accordion' # 设置左侧菜单 折叠样式 # 用户管理 class UserAdmin(object): search_fields = ['username'] # 检索字段 list_display = ['id', 'username', 'password'] # 要显示的字段 list_per_page = 30 # 默认每页显示多少条记录,默认是100条 model_icon = 'fa fa-users' # 左侧小图标 # 标签管理 class TagsAdmin(object): search_fields = ['name'] # 检索字段 list_display = ['id', 'name'] list_filter = ['name'] ordering = ('id',) model_icon = 'fa fa-tags' # 左侧小图标 # 课程管理 class CourseInfoAdmin(object): search_fields = ['name', 'teacher', 'about'] # 检索字段 list_display = ['id', 'show_pic', 'name', 'teacher', 'add_time', 'tags', 'look_num', 'select_num', 'collect_num', 'show_avg_mark'] # 要显示的字段 list_filter = ['add_time', 'tags'] # 分组过滤的字段 ordering = ('id',) # 设置默认排序字段,负号表示降序排序 list_per_page = 30 # 默认每页显示多少条记录,默认是100条 model_icon = 'fa fa-book' # 左侧小图标 list_editable = ['name', 'teacher'] # 可编辑字段 style_fields = {'tags': 'm2m_transfer', 'prizes': 'm2m_transfer'} # 控制字段的显示样式 filter_horizontal = ('tags', 'prizes') # 水平选择编辑多对多字段 def show_pic(self, obj): # 显示书籍封面 if obj.pic.name: text = """ <style type="text/css"> #div1 img{ cursor: pointer; transition: all 0.6s; } #div1 img:hover{ transform: scale(2); } </style> <div id="div1"> <img src="%s" style="width:50px;"/> </div> """ % (self.request.build_absolute_uri('/') + 'media/' + obj.pic.name) return mark_safe(text) return '' def show_avg_mark(self, obj): return obj.avg_mark def save_models(self): flag = self.org_obj is None and 'create' or 'change' if flag == 'create': if self.new_obj.pic.name: self.new_obj.pic.name = f"{self.new_obj.title}.{self.new_obj.pic.name.split('.')[1]}" if flag == 'change' and 'pic' in self.change_message(): if self.org_obj.pic.name: self.org_obj.pic.name = f"{self.org_obj.title}.{self.org_obj.pic.name.split('.')[1]}" super().save_models() show_pic.short_description = '封面' show_avg_mark.short_description = '评分' # 选课表 class UserCourseAdmin(object): search_fields = ['course__name', 'user__name', 'mark'] # 检索字段 list_display = ['user', 'course', 'enroll_time'] # 要显示的字段 list_filter = ['enroll_time'] # 分组过滤的字段 ordering = ('id',) # 设置默认排序字段,负号表示降序排序 list_per_page = 30 # 默认每页显示多少条记录,默认是100条 list_editable = [] # 可编辑字段 fk_fields = ('course', 'user') # 设置显示外键字段 # 书籍评分管理 class RateAdmin(object): search_fields = ['course__name', 'user__name', 'mark'] # 检索字段 list_display = ['course', 'user', 'mark', 'create_time'] # 要显示的字段 list_filter = ['mark', 'create_time'] # 分组过滤的字段 ordering = ('id',) # 设置默认排序字段,负号表示降序排序 list_per_page = 30 # 默认每页显示多少条记录,默认是100条 list_editable = [] # 可编辑字段 fk_fields = ('course', 'user') # 设置显示外键字段 # 书籍收藏管理 class CollectcourseAdmin(object): search_fields = ['course__title', 'user__name'] # 检索字段 list_display = ['course', 'user', 'is_delete', 'create_time'] # 要显示的字段 list_filter = ['is_delete', 'create_time'] # 分组过滤的字段 ordering = ('id',) # 设置默认排序字段,负号表示降序排序 list_per_page = 30 # 默认每页显示多少条记录,默认是100条 list_editable = [] # 可编辑字段 fk_fields = ('course', 'user') # 设置显示外键字段 # 书籍评论管理 class CommentAdmin(object): search_fields = ['course__title', 'user__name'] # 检索字段 list_display = ['user', 'course', 'show_content', 'like_num', 'is_show', 'create_time'] # 要显示的字段 list_filter = ['course', 'is_show', 'create_time'] # 分组过滤的字段 ordering = ('id',) # 设置默认排序字段,负号表示降序排序 list_per_page = 30 # 默认每页显示多少条记录,默认是100条 list_editable = [] # 可编辑字段 fk_fields = ('course', 'user') # 设置显示外键字段 def show_content(self, obj): # 显示评论内容 if not obj.content: return mark_safe('') if len(obj.content) < 20: return mark_safe(obj.content) short_id = f'{obj._meta.db_table}_short_text_{obj.id}' short_text = obj.content[:len(obj.content) // 4] + '......' detail_id = f'{obj._meta.db_table}_detail_text_{obj.id}' detail_text = obj.content text = """<style type="text/css"> #%s,%s {padding:10px;border:1px solid green;} </style> <script type="text/javascript"> function openShutManager(oSourceObj,oTargetObj,shutAble,oOpenTip,oShutTip,oShortObj){ var sourceObj = typeof oSourceObj == "string" ? document.getElementById(oSourceObj) : oSourceObj; var targetObj = typeof oTargetObj == "string" ? document.getElementById(oTargetObj) : oTargetObj; var shortObj = typeof oShortObj == "string" ? document.getElementById(oShortObj) : oShortObj; var openTip = oOpenTip || ""; var shutTip = oShutTip || ""; if(targetObj.style.display!="none"){ if(shutAble) return; targetObj.style.display="none"; shortObj.style.display="block"; if(openTip && shutTip){ sourceObj.innerHTML = shutTip; } } else { targetObj.style.display="block"; shortObj.style.display="none"; if(openTip && shutTip){ sourceObj.innerHTML = openTip; } } } </script> <p id="%s">%s</p> <p><a rel="nofollow" href="###" onclick="openShutManager(this,'%s',false,'点击关闭','点击展开','%s')">点击展开</a></p> <p id="%s" style="display:none"> %s </p> """ % (short_id, detail_id, short_id, short_text, detail_id, short_id, detail_id, detail_text) return mark_safe(text) show_content.short_description = '评论内容' xadmin.site.register(views.CommAdminView, GlobalSettings) xadmin.site.register(views.BaseAdminView, BaseSetting) xadmin.site.register(User, UserAdmin) xadmin.site.register(Tags, TagsAdmin) xadmin.site.register(CourseInfo, CourseInfoAdmin) xadmin.site.register(UserCourse, UserCourseAdmin) xadmin.site.register(RateCourse, RateAdmin) xadmin.site.register(CollectCourse, CollectcourseAdmin) xadmin.site.register(CommentCourse, CommentAdmin)2、修改course\apps.py
代码改为如下:
from django.apps import AppConfig class CourseConfig(AppConfig): name = 'course' verbose_name = "课程推荐系统"3、修改course\__init__.py
代码改为:
default_app_config = 'course.apps.CourseConfig'4、浏览器登录
浏览器访问127.0.0.1:8000/xadmin/
输入账号:root
输入密码:course-root
八、部署到服务器
1、前期工作
部署到ubuntu服务器,把项目放到home目录下,创建数据库course_manager。
在服务器/home/venv/目录下创建一个虚拟环境course_manager:
cd .. cd home/venv/ python3 -m venv course_manager激活虚拟环境:
source course_manager/bin/activate更新pip:
pip install --upgrade pip退回项目根目录:
cd .. cd course_manager批量安装第三方库:
pip install -r requirements.txt -i pypi.tuna.tsinghua.edu.cn/simple数据迁移:
python manage.py migrate创建缓存表:
python manage.py createcachetable创建后台管理员
python manage.py createsuperuser 设置账号为 root 邮箱为 1@qq.com 密码为 book-root数据库图形化工具执行course.sql
2、配置uwsgi.ini
uwsgi.ini内容如下:
[uwsgi] # 使用nginx连接时 使用 socket=0.0.0.0:8102 # 直接作为web服务器使用 #http=127.0.0.1:8102 # 配置工程目录 chdir=/home/course_manager # 配置项目的wsgi目录。相对于工程目录 wsgi-file=course_manager/wsgi.py virtualenv =/home/venv/course_manager #配置进程,线程信息 listen=1024 processes=2 threads=4 enable-threads=True master=True pidfile=uwsgi.pid daemonize=uwsgi.log #django项目修改完文件后自动重启 py-autoreload=1把uwsgi.ini放到根目录下。
导入uwsgi:
pip install uwsgi启动uwsgi:
uwsgi --ini uwsgi.ini查看是否启动成功:
netstat -lnp|grep uwsgi若出现类似:
tcp 0 0 0.0.0.0:8102 0.0.0.0:* LISTEN 927/uwsgi则说明启动成功
2、配置nginx
创建一个course_manager_nginx文件,内容如下:
#设定虚拟主机配置 server { #侦听80端口 listen 80; #listen 443 ssl; #定义使用 www.nginx.cn访问 #ssl on; server_name xxx.xxx.com; #server_name xxx.xxx.xxx.30; #定义服务器的默认网站根目录位置 root /home/course_manager; #ssl_session_timeout 5m; #ssl_certificate /etc/nginx/cert/xxx.pem; #ssl_certificate_key /etc/nginx/cert/xxx.key; #ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; #ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #ssl_prefer_server_ciphers on; #设定本虚拟主机的访问日志 #access_log logs/nginx.access.log main; #默认请求 location / { #倒入了uwsgi的配置 include uwsgi_params; client_max_body_size 50m; #连接uwsgi的超时时间 # uwsgi_connect_timeout 30; #设定了uwsig服务器位置 uwsgi_pass 127.0.0.1:8102; } location /static{ alias /home/course_manager/static; } location /media { alias /home/course_manager/media; } }文件放到/etc/nginx/sites-available下面。
然后通过以下命令映射到/etc/nginx/sites-enabled
ln -s /etc/nginx/sites-available/course_manager_nginx /etc/nginx/sites-enabled/course_manager_nginxnginx重启:
nginx -s reload在浏览器中访问网站就可以看到课程了。
本文共计3502个文字,预计阅读时间需要15分钟。
一、搭建项目+Python解释器版本使用3.7.8。
二、创建虚拟环境+在D盘下创建文件夹my_work,然后在其中创建两个文件夹:pro和venv。
三、Win+R输入cmd进入文件夹venv,执行以下命令创建虚拟环境:
bashpython -m venv venv一、搭建项目
python解释器版本使用3.7.8。
1、创建虚拟环境
在d盘下创建一个文件夹my_work,然后在里面创建两个文件夹:pro和venv。
win+R输入cmd进入文件夹venv,然后执行以下命令创建虚拟环境:
python -m venv course_manager激活虚拟环境:
cd course_manager cd Scripts activate导入django:
pip install Django==3.0.7 -i pypi.mirrors.ustc.edu.cn/simple/ pip install PyMySQL==0.9.2 -i pypi.mirrors.ustc.edu.cn/simple/ pip install xadmin-django==3.0.2 -i pypi.mirrors.ustc.edu.cn/simple/ pip install mysqlclient==2.0.1 -i pypi.mirrors.ustc.edu.cn/simple/ pip install numpy==1.21.6 -i pypi.mirrors.ustc.edu.cn/simple/然后进入pro目录
cd .. cd .. cd .. cd pro2、创建项目
执行命令:
django-admin startproject course_manager3、创建子应用
切换到项目根目录:
cd course_manager创建子应用:
python manage.py startapp course自此项目创建完成。
二、settings.py配置
1、创建数据库
使用MySQL可视化工具创建一个数据库course_manager。
2、PyCharm打开项目
使用PyCharm打开项目:file->open.
在项目根目录下创建以下文件夹:
static、media、imgs、log、templates。
其中media中再创建一个文件夹course_cover存放课程封面。
选中templates->右键->Make Directory as- >Template Folder.
3、配置项目虚拟环境
4、允许所有网站访问
在course_manager\settings.py中做修改:
ALLOWED_HOSTS = ['*']5、添加子应用
在course_manager\settings.py中的INSTALLED_APPS加入子应用course,如下:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'xadmin', 'crispy_forms', 'course' ]6、添加templates目录
在course_manager\settings.py中的TEMPLATES中DIRS改为:
'DIRS': [os.path.join(BASE_DIR, 'templates')],7、使用mysql数据库
把在course_manager\settings.py中的DATABASES注释掉,改为:
ip = '127.0.0.1' DATABASE_NAME = 'course_manager' # mysql数据库名称 DATABASE_USER = 'root' # mysql数据库用户名 DATABASE_PASS = 'xxx' # mysql数据库密码 DATABASE_HOST = ip # mysql数据库IP DATABASE_PORT = 3306 # mysql数据库端口 # 配置数据库 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # 修改数据库为MySQL,并进行配置 'NAME': DATABASE_NAME, # 'USER': DATABASE_USER, # 用户名 'PASSWORD': DATABASE_PASS, # 密码 'HOST': DATABASE_HOST, 'PORT': DATABASE_PORT, 'OPTIONS': {'charset': 'utf8mb4', } } }8、使用中文
把course_manager\settings.py的LANGUAGE_CODE、TIME_ZONE和USE_TZ改为:
LANGUAGE_CODE = 'zh-hans' # 使用中国时区 TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_L10N = True USE_TZ = False9、配置静态文件路由
把course_manager\settings.py的STATIC_URL改为:
STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ] # STATIC_ROOT = os.path.join(BASE_DIR, 'static') # 收集静态文件时打开,然后关闭STATICFILES_DIRS MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_USER_ICON = os.path.join(BASE_DIR, 'media/user_icon')三、models.py数据表
在course\models.py中创建用户表、课程类别表、课程表、用户选课表、评分表、收藏表、评论表:
from django.db import models # 用户表 class User(models.Model): username = models.CharField(max_length=255, unique=True, verbose_name="账号") password = models.CharField(default='123456', max_length=32, verbose_name="密码") class Meta: db_table = 'user' verbose_name_plural = "用户" verbose_name = "用户" def __str__(self): return self.username # 课程类别表 class Tags(models.Model): name = models.CharField(max_length=200, verbose_name="类别") class Meta: db_table = 'tags' verbose_name = "课程类别" verbose_name_plural = "课程类别" def __str__(self): return self.name # 课程表 class CourseInfo(models.Model): name = models.CharField(verbose_name="课程名", max_length=255) course_code = models.CharField(unique=True, verbose_name="课程编号", max_length=255) teacher = models.CharField(verbose_name="讲师", max_length=255) about = models.TextField(verbose_name="描述") pic = models.FileField(verbose_name="封面图片", max_length=64, upload_to='course_cover') tags = models.ForeignKey( Tags, on_delete=models.CASCADE, verbose_name="类别", related_name="tags", blank=True, null=True, ) select_num = models.IntegerField(verbose_name="选修人数", default=0) look_num = models.IntegerField(verbose_name="浏览人数", default=0) collect_num = models.IntegerField(verbose_name="收藏人数", default=0) course_url = models.TextField(verbose_name="课程网址", default='www.xuetangx.com/') add_time = models.DateTimeField(verbose_name="创建时间", auto_now_add=True) class Meta: db_table = 'course_info' verbose_name = "课程" verbose_name_plural = "课程" def __str__(self): return self.name # 用户选课表 class UserCourse(models.Model): course = models.ForeignKey( CourseInfo, to_field='course_code', on_delete=models.CASCADE, blank=True, null=True, verbose_name="课程id" ) user = models.ForeignKey( User, on_delete=models.CASCADE, blank=True, null=True, verbose_name="用户id", ) enroll_time = models.DateTimeField(verbose_name="选修时间", auto_now_add=True) class Meta: db_table = 'user_course' verbose_name = "用户选课信息" verbose_name_plural = verbose_name # 评分表 class RateCourse(models.Model): course = models.ForeignKey( CourseInfo, to_field='course_code', on_delete=models.CASCADE, related_name='rate_course', blank=True, null=True, verbose_name="课程id" ) user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True, verbose_name="用户id") mark = models.FloatField(verbose_name="评分") create_time = models.DateTimeField(verbose_name="添加时间", auto_now_add=True) class Meta: db_table = 'rate_course' verbose_name = "评分信息" verbose_name_plural = verbose_name # 收藏表 class CollectCourse(models.Model): course = models.ForeignKey( CourseInfo, to_field='course_code', on_delete=models.CASCADE, related_name='collect_course', blank=True, null=True, verbose_name="课程id" ) user = models.ForeignKey( User, on_delete=models.CASCADE, blank=True, null=True, verbose_name="用户id", ) is_delete = models.BooleanField(default=False, verbose_name='是否取消') create_time = models.DateTimeField(verbose_name="收藏时间", auto_now_add=True) class Meta: db_table = 'collect_course' verbose_name = "课程收藏" verbose_name_plural = verbose_name # 评论表 class CommentCourse(models.Model): course = models.ForeignKey( CourseInfo, to_field='course_code', on_delete=models.CASCADE, related_name='comment_course', blank=True, null=True, verbose_name="课程id" ) user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True, verbose_name="用户") content = models.TextField(verbose_name="评论内容") create_time = models.DateTimeField(verbose_name='评论时间', auto_now_add=True) like_num = models.IntegerField(verbose_name="点赞数", default=0) like_users = models.TextField(null=True, blank=True, default=None, verbose_name="点赞用户") is_show = models.BooleanField(default=True, verbose_name='是否显示') class Meta: db_table = 'comment_course' verbose_name = "课程评论" verbose_name_plural = verbose_name四、urls.py路由配置
1、修改course_manager\urls.py
在course下创建一个urls.py,并让course_manager\urls.py分配路由,
其中course_manager\urls.py改为:
import xadmin from django.urls import path, re_path, include from django.views.generic import RedirectView from django.views.static import serve from django.conf import settings urlpatterns = [ path('xadmin/', xadmin.site.urls), path("", include("course.urls")), # favicon.cio re_path(r'^favicon\.ico$', RedirectView.as_view(url=r'media/favicon.ico')), re_path(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}), # re_path(r'^static/(?P<path>.*)$', serve, {'document_root': settings.STATICFILES_DIRS}), # 收集静态文件时关闭 path(r'^static/(?P<path>.*)$', serve, {'document_root': settings.STATIC_ROOT}), # 收集静态文件时打开,然后关闭STATICFILES_DIRS ]2、修改course\urls.py
先改为:
from django.urls import path, re_path from course import views urlpatterns = [ ]后面会添加各种路由。
3、数据迁移
在pycharm左下角的Terminal里执行数据迁移命令
python manage.py makemigrations python manage.py migrate4、创建缓存表
python manage.py createcachetable5、收集静态文件
先把course_manager\settings.py中的静态文件路由改为:
STATIC_URL = '/static/' # STATICFILES_DIRS = [ # os.path.join(BASE_DIR, 'static'), # ] STATIC_ROOT = os.path.join(BASE_DIR, 'static') # 收集静态文件时打开,然后关闭STATICFILES_DIRS然后执行:
python manage.py collectstatic执行成功后,把course_manager\settings.py中的静态文件路由改为:
STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ] # STATIC_ROOT = os.path.join(BASE_DIR, 'static') # 收集静态文件时打开,然后关闭STATICFILES_DIRS把course_manager\urls.py改为:
import xadmin from django.urls import path, re_path, include from django.views.generic import RedirectView from django.views.static import serve from django.conf import settings urlpatterns = [ path('xadmin/', xadmin.site.urls), path("", include("course.urls")), # favicon.cio re_path(r'^favicon\.ico$', RedirectView.as_view(url=r'media/favicon.ico')), re_path(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}), re_path(r'^static/(?P<path>.*)$', serve, {'document_root': settings.STATICFILES_DIRS}), # 收集静态文件时关闭 # path(r'^static/(?P<path>.*)$', serve, {'document_root': settings.STATIC_ROOT}), # 收集静态文件时打开,然后关闭STATICFILES_DIRS ]6、创建后台管理员
python manage.py createsuperuser 设置账号为 root 邮箱为 1@qq.com 密码为 course-root五、导入基础数据
把根目录下的course.sql在mysql可视化工具中执行即可。
六、核心代码
1、static创建文件夹
在static目录下创建三个文件夹image、css、js和fonts用来存放前端需要使用到的文件。
2、base.html前端框架
在目录templates下创建前端页面框架base.html,代码如下:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta fontawesome.dashgame.com/ 图标字体网站 # 基础设置 class BaseSetting(object): enable_themes = True # 使用主题 use_bootswatch = True # 全局设置 class GlobalSettings(object): site_title = '课程管理系统' # 标题 site_footer = mark_safe('课程推荐系统') # 页尾 site_url = '/' menu_style = 'accordion' # 设置左侧菜单 折叠样式 # 用户管理 class UserAdmin(object): search_fields = ['username'] # 检索字段 list_display = ['id', 'username', 'password'] # 要显示的字段 list_per_page = 30 # 默认每页显示多少条记录,默认是100条 model_icon = 'fa fa-users' # 左侧小图标 # 标签管理 class TagsAdmin(object): search_fields = ['name'] # 检索字段 list_display = ['id', 'name'] list_filter = ['name'] ordering = ('id',) model_icon = 'fa fa-tags' # 左侧小图标 # 课程管理 class CourseInfoAdmin(object): search_fields = ['name', 'teacher', 'about'] # 检索字段 list_display = ['id', 'show_pic', 'name', 'teacher', 'add_time', 'tags', 'look_num', 'select_num', 'collect_num', 'show_avg_mark'] # 要显示的字段 list_filter = ['add_time', 'tags'] # 分组过滤的字段 ordering = ('id',) # 设置默认排序字段,负号表示降序排序 list_per_page = 30 # 默认每页显示多少条记录,默认是100条 model_icon = 'fa fa-book' # 左侧小图标 list_editable = ['name', 'teacher'] # 可编辑字段 style_fields = {'tags': 'm2m_transfer', 'prizes': 'm2m_transfer'} # 控制字段的显示样式 filter_horizontal = ('tags', 'prizes') # 水平选择编辑多对多字段 def show_pic(self, obj): # 显示书籍封面 if obj.pic.name: text = """ <style type="text/css"> #div1 img{ cursor: pointer; transition: all 0.6s; } #div1 img:hover{ transform: scale(2); } </style> <div id="div1"> <img src="%s" style="width:50px;"/> </div> """ % (self.request.build_absolute_uri('/') + 'media/' + obj.pic.name) return mark_safe(text) return '' def show_avg_mark(self, obj): return obj.avg_mark def save_models(self): flag = self.org_obj is None and 'create' or 'change' if flag == 'create': if self.new_obj.pic.name: self.new_obj.pic.name = f"{self.new_obj.title}.{self.new_obj.pic.name.split('.')[1]}" if flag == 'change' and 'pic' in self.change_message(): if self.org_obj.pic.name: self.org_obj.pic.name = f"{self.org_obj.title}.{self.org_obj.pic.name.split('.')[1]}" super().save_models() show_pic.short_description = '封面' show_avg_mark.short_description = '评分' # 选课表 class UserCourseAdmin(object): search_fields = ['course__name', 'user__name', 'mark'] # 检索字段 list_display = ['user', 'course', 'enroll_time'] # 要显示的字段 list_filter = ['enroll_time'] # 分组过滤的字段 ordering = ('id',) # 设置默认排序字段,负号表示降序排序 list_per_page = 30 # 默认每页显示多少条记录,默认是100条 list_editable = [] # 可编辑字段 fk_fields = ('course', 'user') # 设置显示外键字段 # 书籍评分管理 class RateAdmin(object): search_fields = ['course__name', 'user__name', 'mark'] # 检索字段 list_display = ['course', 'user', 'mark', 'create_time'] # 要显示的字段 list_filter = ['mark', 'create_time'] # 分组过滤的字段 ordering = ('id',) # 设置默认排序字段,负号表示降序排序 list_per_page = 30 # 默认每页显示多少条记录,默认是100条 list_editable = [] # 可编辑字段 fk_fields = ('course', 'user') # 设置显示外键字段 # 书籍收藏管理 class CollectcourseAdmin(object): search_fields = ['course__title', 'user__name'] # 检索字段 list_display = ['course', 'user', 'is_delete', 'create_time'] # 要显示的字段 list_filter = ['is_delete', 'create_time'] # 分组过滤的字段 ordering = ('id',) # 设置默认排序字段,负号表示降序排序 list_per_page = 30 # 默认每页显示多少条记录,默认是100条 list_editable = [] # 可编辑字段 fk_fields = ('course', 'user') # 设置显示外键字段 # 书籍评论管理 class CommentAdmin(object): search_fields = ['course__title', 'user__name'] # 检索字段 list_display = ['user', 'course', 'show_content', 'like_num', 'is_show', 'create_time'] # 要显示的字段 list_filter = ['course', 'is_show', 'create_time'] # 分组过滤的字段 ordering = ('id',) # 设置默认排序字段,负号表示降序排序 list_per_page = 30 # 默认每页显示多少条记录,默认是100条 list_editable = [] # 可编辑字段 fk_fields = ('course', 'user') # 设置显示外键字段 def show_content(self, obj): # 显示评论内容 if not obj.content: return mark_safe('') if len(obj.content) < 20: return mark_safe(obj.content) short_id = f'{obj._meta.db_table}_short_text_{obj.id}' short_text = obj.content[:len(obj.content) // 4] + '......' detail_id = f'{obj._meta.db_table}_detail_text_{obj.id}' detail_text = obj.content text = """<style type="text/css"> #%s,%s {padding:10px;border:1px solid green;} </style> <script type="text/javascript"> function openShutManager(oSourceObj,oTargetObj,shutAble,oOpenTip,oShutTip,oShortObj){ var sourceObj = typeof oSourceObj == "string" ? document.getElementById(oSourceObj) : oSourceObj; var targetObj = typeof oTargetObj == "string" ? document.getElementById(oTargetObj) : oTargetObj; var shortObj = typeof oShortObj == "string" ? document.getElementById(oShortObj) : oShortObj; var openTip = oOpenTip || ""; var shutTip = oShutTip || ""; if(targetObj.style.display!="none"){ if(shutAble) return; targetObj.style.display="none"; shortObj.style.display="block"; if(openTip && shutTip){ sourceObj.innerHTML = shutTip; } } else { targetObj.style.display="block"; shortObj.style.display="none"; if(openTip && shutTip){ sourceObj.innerHTML = openTip; } } } </script> <p id="%s">%s</p> <p><a rel="nofollow" href="###" onclick="openShutManager(this,'%s',false,'点击关闭','点击展开','%s')">点击展开</a></p> <p id="%s" style="display:none"> %s </p> """ % (short_id, detail_id, short_id, short_text, detail_id, short_id, detail_id, detail_text) return mark_safe(text) show_content.short_description = '评论内容' xadmin.site.register(views.CommAdminView, GlobalSettings) xadmin.site.register(views.BaseAdminView, BaseSetting) xadmin.site.register(User, UserAdmin) xadmin.site.register(Tags, TagsAdmin) xadmin.site.register(CourseInfo, CourseInfoAdmin) xadmin.site.register(UserCourse, UserCourseAdmin) xadmin.site.register(RateCourse, RateAdmin) xadmin.site.register(CollectCourse, CollectcourseAdmin) xadmin.site.register(CommentCourse, CommentAdmin)2、修改course\apps.py
代码改为如下:
from django.apps import AppConfig class CourseConfig(AppConfig): name = 'course' verbose_name = "课程推荐系统"3、修改course\__init__.py
代码改为:
default_app_config = 'course.apps.CourseConfig'4、浏览器登录
浏览器访问127.0.0.1:8000/xadmin/
输入账号:root
输入密码:course-root
八、部署到服务器
1、前期工作
部署到ubuntu服务器,把项目放到home目录下,创建数据库course_manager。
在服务器/home/venv/目录下创建一个虚拟环境course_manager:
cd .. cd home/venv/ python3 -m venv course_manager激活虚拟环境:
source course_manager/bin/activate更新pip:
pip install --upgrade pip退回项目根目录:
cd .. cd course_manager批量安装第三方库:
pip install -r requirements.txt -i pypi.tuna.tsinghua.edu.cn/simple数据迁移:
python manage.py migrate创建缓存表:
python manage.py createcachetable创建后台管理员
python manage.py createsuperuser 设置账号为 root 邮箱为 1@qq.com 密码为 book-root数据库图形化工具执行course.sql
2、配置uwsgi.ini
uwsgi.ini内容如下:
[uwsgi] # 使用nginx连接时 使用 socket=0.0.0.0:8102 # 直接作为web服务器使用 #http=127.0.0.1:8102 # 配置工程目录 chdir=/home/course_manager # 配置项目的wsgi目录。相对于工程目录 wsgi-file=course_manager/wsgi.py virtualenv =/home/venv/course_manager #配置进程,线程信息 listen=1024 processes=2 threads=4 enable-threads=True master=True pidfile=uwsgi.pid daemonize=uwsgi.log #django项目修改完文件后自动重启 py-autoreload=1把uwsgi.ini放到根目录下。
导入uwsgi:
pip install uwsgi启动uwsgi:
uwsgi --ini uwsgi.ini查看是否启动成功:
netstat -lnp|grep uwsgi若出现类似:
tcp 0 0 0.0.0.0:8102 0.0.0.0:* LISTEN 927/uwsgi则说明启动成功
2、配置nginx
创建一个course_manager_nginx文件,内容如下:
#设定虚拟主机配置 server { #侦听80端口 listen 80; #listen 443 ssl; #定义使用 www.nginx.cn访问 #ssl on; server_name xxx.xxx.com; #server_name xxx.xxx.xxx.30; #定义服务器的默认网站根目录位置 root /home/course_manager; #ssl_session_timeout 5m; #ssl_certificate /etc/nginx/cert/xxx.pem; #ssl_certificate_key /etc/nginx/cert/xxx.key; #ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; #ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #ssl_prefer_server_ciphers on; #设定本虚拟主机的访问日志 #access_log logs/nginx.access.log main; #默认请求 location / { #倒入了uwsgi的配置 include uwsgi_params; client_max_body_size 50m; #连接uwsgi的超时时间 # uwsgi_connect_timeout 30; #设定了uwsig服务器位置 uwsgi_pass 127.0.0.1:8102; } location /static{ alias /home/course_manager/static; } location /media { alias /home/course_manager/media; } }文件放到/etc/nginx/sites-available下面。
然后通过以下命令映射到/etc/nginx/sites-enabled
ln -s /etc/nginx/sites-available/course_manager_nginx /etc/nginx/sites-enabled/course_manager_nginxnginx重启:
nginx -s reload在浏览器中访问网站就可以看到课程了。

