第1章 强力django+杀手级xadmin 打造上线标准的在线教育平台-导学
课程简介和学习安排
1-1 强力django+杀手级xadmin 打造上线标准的在线教育平台
第2章 开发环境搭建-linux
本章节将会带领大家在windows上通过虚拟机安装linux,并在linux上安装python、pycharm、navicat、python和虚拟环境等课程必须的软件
2-1 课程中会用到的开发环境介绍 (03:06)
- IDE: pycharm
- 数据库: mysql, navicat
- 编程语言: python3.7
- 虚拟环境: virtualenvwrapper
2-2 如何在windows上安装linux-上 (13:39)
2-3 如何在windows上安装linux-下 (14:24)
- 安装的是 优麒麟
https://www.ubuntukylin.com/
2-4 python的安装和配置 (12:30)
- python的依赖包
http://projectsedu.com/2019/11/15/centos7-%E4%B8%8B%E9%80%9A%E8%BF%87nginx-uwsgi%E9%83%A8%E7%BD%B2django%E5%BA%94%E7%94%A8/
2-5 虚拟环境的安装和配置 (15:07)
- 安装虚拟环境
http://projectsedu.com/2019/11/15/centos7-%E4%B8%8B%E9%80%9A%E8%BF%87nginx-uwsgi%E9%83%A8%E7%BD%B2django%E5%BA%94%E7%94%A8/
- 安装 virtualenvwrapper, 后面的中括号,加上之后是使用的是豆瓣源,速度更快.
pip3 install virtualenvwrapper [-i https://pypi.douban.com/simple]
- 查找位置
sudo find / -name virtualenvwrapper.sh 查找位置
sudo find / -name python3 查找位置
- 加到 .bashrc 最后
VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
export WORKON_HOME=$HOME/.virtualenv
source /home/zq/.local/bin/virtualenvwrapper.sh
- 进入虚拟环境
source .bashrc
mkvirtualenv test # 新建虚拟环境
workon # 列出所有虚拟环境
workon 虚拟环境名称 # 进入虚拟环境
deactivate # 退出虚拟环境
rmvirtualenv 虚拟环境名称 # 删除虚拟环境
2-6 mysql和navicat的安装和配置 (19:24)
- 安装mysql,
Ubuntu下
sudo apt-get install mysql-server
安装好之后 sudo vim /etc/mysql/debian.cnf
拿到 user
和 password
来登录数据库.
mysql -udebian-sys-maint -pBZyX3QSuTEsVyqxQ
可以进入.
2-7 pycharm的安装和配置 (07:46)
第3章 开发环境搭建 -windows
本章节将会带领大家在windows上上安装python、pycharm、navicat、python和虚拟环境等课程必须的软件
3-1 课程中会用到的开发环境介绍 (03:13)
3-2 python、mysql、navicat和pycharm的安装和配置 (23:07)
第4章 开发环境搭建-mac
本章节通过文档的形式详细讲解如何在mac上安装必须的软件:python、pycharm、navicat、python和虚拟环境
4-1 开发环境搭建指南-mac
第5章 Navicat和Pycharm的基础
课程的整个代码开发都是在pycharm中完成的,所以这里会用单独的章节专门讲解pycharm的使用,也会介绍navicat的简单使用
5-1 navicat的简单使用 (11:13)
5-2 pycharm简单介绍(很重要!!!) (11:29)
5-3 如何在pycharm中调试代码 (07:42)
5-4 pycharm中常用的快捷键(很重要!!!) (11:06)
第6章 留言板快速开发【用一个小项目巩固Django基础知识】
通过django简单实现一个留言板功能来回顾django的基础知识, 包括settings的配置、 url配置、 view逻辑、 model设计和templates的显示
6-1 django目录结构解析-1 (12:32)
- 进入虚拟环境之后安装
pip install django==2.2 -i https://pypi.douban.com/simple
6-2 django目录结构解析-2 (11:44)
如上,在原来的基础上,新加了一些 目录
apps
这个目录是自己新建的,把自己编写的内部的应用都放在这个目录中djangotest
主目录extra_apps
自己新建的,用来放后期引入的外部类media
自己新建,放一些上传类的文件static
自己新建,放一些静态文件requirements.txt
自己新建, 里面写所依赖的包的版本号
6-3 配置url和静态文件 (21:30)
-
在
apps/message_form
下新建目录templates
, 再在templates
下新建message_form
目录,加入message_form.html
-
现在应用
apps/message_form/views.py
中加一个函数
from django.shortcuts import render
# Create your views here.
def message_form(request):
return render(request, "message_form/message_form.html")
- 在
apps/message_form
下新加一个urls.py
, 加入如下代码
from django.urls import path
from . import views
app_name = "message_form"
urlpatterns = [
path("", views.message_form)
]
- 在 主目录
Message/urls.py
中修改如下
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('message_form/', include('apps.message_form.urls'))
]
- 在浏览器中, 打开
http://127.0.0.1:8000/message_form/
, 即可看到页面.
注意静态文件 static 的位置
- 如果用默认的
static
,则直接在 应用message_form
下新建一个static
即可. - 如果是放在外层统一管理, 则需要在
Message/settings.py
下, 最后行, 加入STATICFILES_DIRS
, 这个参数,是告诉系统,静态文件在那里查找.BASE_DIR
是之前定义的项目所在文件夹.
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
6-4 orm和model表设计-1 (11:45)
- 按照 mysqlclient
pip install mysqlclient
- 修改
Message/settings.py
的 数据库配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mxonline',
'USER': 'root',
'PASSWORD': 'root',
'HOST': '127.0.0.1'
}
}
- 在目录下运行命令
python3 manage.py makemigrations
- 继续
python3 manage.py migrate
, 这样,django
默认的表就自动导入到mysql
的mxonline
数据库中了
6-5 orm和model表设计-2 (10:41)
- 在
message_form
下的models.py
中新建model
from django.db import models
# Create your models here.
class Message(models.Model):
name = models.CharField(max_length=20, verbose_name="姓名")
email = models.EmailField(verbose_name="邮箱")
address = models.CharField(max_length=100, verbose_name="联系地址")
message = models.TextField(verbose_name="留言信息")
class Meta:
verbose_name = "留言信息"
verbose_name_plural = verbose_name
# db_table = "my_message"
- 这个
app
一定要在Message
下settings.py
中的INSTALLED_APPS
中注册一下. 不然无法生成表
INSTALLED_APPS = [
'apps.message_form.apps.MessageFormConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
python3 manage.py makemigrations
python3 manage.py migrate
生成表
文档: https://docs.djangoproject.com/zh-hans/2.0/ref/models/fields/
6-6 model进行增、删、改、查-1 (12:20)
- 在
message_form
下的views.py
中, 取到models.py
中的数据
from django.shortcuts import render
# Create your views here.
from apps.message_form.models import Message # 导入 models
def message_form(request):
all_messages = Message.objects.all() # 获取所有数据
for message in all_messages:
print(message.name)
return render(request, 'message_form/message_form.html')
all_messages = Message.objects.all()
返回的是一个querset
对象, 可以进行for循环
和切片操作
, 但是querset
本身并没有执行sql操作.- 测试输出
sql语句
, 这里是查找所有字段
all_messages = Message.objects.all()
sliced_query = Message.objects.all()[:1] #切片操作
print(all_messages.query)
print(sliced_query.query)
SELECT `message`.`id`, `message`.`name`, `message`.`email`, `message`.`address`, `message`.`message` FROM `message`
SELECT `message`.`id`, `message`.`name`, `message`.`email`, `message`.`address`, `message`.`message` FROM `message` LIMIT 1
6-7 model进行增、删、改、查-2 (12:04)
Message.objects.all()
查询数据库中的所有字段- 用
filter
来输入条件查找
from django.shortcuts import render
# Create your views here.
from apps.message_form.models import Message
def message_form(request):
#2. filter
all_messages = Message.objects.filter(name="bobby1")
print(all_messages.query)
for message in all_messages:
print(message.name)
return render(request, 'message_form/message_form.html')
- 用
get
来查找数据.get
返回的是一个对象,数据不存在或者多条数据存在会抛出异常
from django.shortcuts import render
# Create your views here.
from apps.message_form.models import Message
def message_form(request):
# get 返回的是一个对象,数据不存在或者多条数据存在会抛出异常
try:
message = Message.objects.get(name="bobby1")
print(message.name)
except Message.DoesNotExist as e:
pass
except Message.MultipleObjectsReturned as e:
pass
return render(request, 'message_form/message_form.html')
- 删除数据
delete
方法
all_messages = Message.objects.filter(name="bobby")
all_messages.delete() # 删除所有数据
save
插入数据, ①如果主键存在则更新,②如果不存在则插入.
# 部分核心代码
# save 插入数据
message = Message()
message.name = "bobby"
message.email = "bobby@imooc.com"
message.address = "北京市"
message.message = "留言"
message.save()
6-8 从前端html页面提取出数据并保存到数据库中 (14:08)
- 把
form
的action
改成我们自己的地址action='message_form'
- 提交之后,报错
Forbidden (403) CSRF verification failed. Request aborted.
- 需要在
html
的form
下加一行代码
<!-- 部分代码 -->
...
...
<input type="submit" class="button" value="提交"/>
</label>
{% csrf_token %}
</form>
- 在一个方法中, 区别是
post
还是get
, 然后加入数据库
# get 展示页面, post 获取数据
def message_form(request):
if request.method == "POST":
# 从html中提取数据保存到数据库中
name = request.POST.get("name", "")
email = request.POST.get("email", "")
address = request.POST.get("address", "")
messagecontent = request.POST.get("message", "")
message = Message()
message.name = name
message.email = email
message.address = address
message.message = messagecontent
message.save()
return render(request, 'message_form/message_form.html')
6-9 django的template数据展示 (18:49)
- 获取值再给到模板
#部分代码
if request.method == "GET":
all_message = Message.objects.all()
if all_message:
message = all_message[0]
return render(request, 'message_form/message_form.html',{
"message": message
})
- 这里使用
{'message': message}
, 也有一种方法, 可以一步把所有的都给到页面.return render(request, "message_form", locals()"
, 不建议这么写. - 展示到
模板
中<input id="name" value="{{ message.name }}" />
, 用{{ message.name }}
的方法 第一步
代码优化
if request.method == "GET":
var_dict = {}
all_message = Message.objects.all()
if all_message:
message = all_message[0]
var_dict = {
"message": message
}
return render(request, 'message_form/message_form.html', var_dict)
- 在
views.py
的方法中,最后必须返回一个return
, 所以上面的post
最后还要优化一下, 最后加上return
if request.method == "POST":
# 从html中提取数据保存到数据库中
name = request.POST.get("name", "")
email = request.POST.get("email", "")
address = request.POST.get("address", "")
message_text = request.POST.get("message", "")
message = Message()
message.name = name
message.email = email
message.address = address
message.message = message_text
message.save()
return render(request, 'message_form/message_form.html', {
"message": message
})
- 模板的文档
https://docs.djangoproject.com/zh-hans/2.0/ref/templates/builtins/
- 模板的
if
用法<input value="{% if message.name == 'bobby' %}bobbytest{% endif %}" type="text" />
ifequal
方法<input value="{% ifequal message.name|slice:'2' 'bo' %}bobbytest{% endifequal %}" type="text" />
如果前面2个字母是bo
的话,则显示bobbytest
, 不然则不显示.
第7章 需求分析和表结构设计–开始搞一个大项目
对系统进行需求分析, 然后设计出django app, 然后对每个app设计相应的django model数据表。系统共有四个app, users处理用户相关;courses处理课程相关;organization处理课程机构相关;operation处理用户操作相关
7-1 需求分析和app设计 (15:48)
-
users 用户相关
-
courses 课程相关
-
organization 机构相关
-
operation 用户操作相关
-
目录结构和
requirements.txt
-
数据库配置 和 静态文件配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mxonline',
'USER': 'root',
'PASSWORD': 'root',
'HOST': '127.0.0.1'
}
}
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
7-2 新建项目和apps (07:22)
python3 manage.py startapp users
python3 manage.py startapp course
python3 manage.py startapp organization
python3 manage.py startapp operation
配置到
settings.py
中
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'apps.course.apps.CourseConfig',
'apps.usrs.apps.UsrsConfig',
'apps.operation.apps.OperationConfig',
'apps.organization.apps.OrganizationConfig'
]
7-3 自定义userprofile表覆盖默认的user表 (20:48)
系统自带的用户表无法满足我们项目的需要, 这里需要写方法重载之前的用户表
- 在
Users.py
下的models.py
中定义UserProfile
class UserProfile(AbstractUser):
nick_name = models.CharField(max_length=50, verbose_name="昵称", default="")
birthday = models.DateField(verbose_name="生日", null=True, blank=True)
gender = models.CharField(verbose_name="性别", choices=GENDER_CHOICES, max_length=6)
address = models.CharField(max_length=100, verbose_name="地址", default="")
mobile = models.CharField(max_length=11, unique=True, verbose_name="手机号码")
image = models.ImageField(upload_to="head_image/%Y/%m", default="default.jpg")
class Meta:
verbose_name = "用户信息"
verbose_name_plural = verbose_name
def __str__(self):
if self.nick_name:
return self.nick_name
else:
return self.username
我们重载完了 原来的
auth_user
表, 要告诉系统, 以后要用我们自己定义的表, 而不是系统默认的auth_user
表. 在Mxonline/settings.py
总配置下, 新加一个字段
# 111行
AUTH_USER_MODEL = "user.UserProfile"
-
删除之前数据库中的所有表
-
python3 manage.py makemigrations
-
python3 manage.py migrate
因为里面有 图片的
ImageField
, 所以系统提示要安装pip install pillow -i https://pypi.douban.com/simple
python3 manage.py makemigrations
python3 manage.py migrate
7-4 如何避免循环import不同apps中的model (05:15)
- 下一层不能导入上一层
7-5 course相关的表结构设计 - 1 (17:23)
courses models.py
又引入其他4个实体
- Course 课程基本信息
- Lesson 章节信息
- Video 视频
- CourseResource 课程资源
在建立 模型的时候,发现有一些字段是每个模型都需要的,例如 添加时间 等.所以, 可以写一个基本类,来继承
models.Model
, 然后让我们的其他模型都继承这个类. 这样就可以实现代码的复用. 这里根据7-4
的层级, 把这个父类放在Users
下面的models
里面
# 基础类
class BaseModel(models.Model):
add_time = models.DateField(default=datetime.now, verbose_name="添加时间")
class Meta:
abstract = True
然后在其他模型中,导入这个父类.
from apps.users.models import BaseModel
...
class Course(BaseModel):
pass
...
7-6 course相关的表结构设计 - 2 (16:32)
course 课程基本信息 model
设计
class Course(BaseModel):
name = models.CharField(verbose_name="课程名", max_length=50)
desc = models.CharField(verbose_name="课程描述", max_length=300)
learn_times = models.IntegerField(default=0, verbose_name="学习时长(分钟数)")
degree = models.CharField(verbose_name="难度", choices=(("cj", "初级"), ("zj", "中级"), ("gj", "高级")), max_length=2)
students = models.IntegerField(default=0, verbose_name="学习人数")
fav_nums = models.IntegerField(default=0, verbose_name="收藏人数")
click_nums = models.IntegerField(default=0, verbose_name="点击数")
category = models.CharField(default=u"后端开发", max_length=20, verbose_name="课程类别")
tag = models.CharField(default="", verbose_name="课程标签", max_length=10)
youneed_know = models.CharField(default="", max_length=300, verbose_name="课程须知")
teachar_tell = models.CharField(default="", max_length=300, verbose_name="老师告诉你")
detail = models.TextField(verbose_name="课程详情")
image = models.ImageField(upload_to="courses/%Y/%m", verbose_name="封面图", max_length=100)
class Meta:
verbose_name = "课程信息"
verbose_name_plural = verbose_name
Lesson 章节信息 model
设计
外键数据的删除情况
course = models.ForeignKey(Course, on_delete=models.CASCADE)
这里的on_delete
分二种常见的情况
on_delete=models.CASCADE
这里表示如果 课程被删除了, 那么自己这个课程章节也删除.
on_delete=models.SET_NULL
, 这种就是 对应的外键数据删除了, 自己的数据不动, 而且后面也要加上on_delete=models.SET_NULL, null=True, blank=True
例如:course = models.ForeignKey(Course, on_delete=models.SET_NULL, null=True, blank=True)
# 课程章节
class Lesson(BaseModel):
# on_delete 表示对应的外键数据被删除后, 当前的数据应该怎么办.
# on_delete=models.CASCADE 这里表示如果 课程被删除了, 那么自己这个课程章节也删除.
# 还有一种 on_delete=models.SET_NULL, 这种就是 对应的外键数据删除了, 自己的数据不动
course = models.ForeignKey(Course, on_delete=models.CASCADE)
name = models.CharField(max_length=100, verbose_name=u"章节名")
learn_times = models.IntegerField(default=0, verbose_name=u"学习时长(分钟数)")
class Meta:
verbose_name = "课程章节"
verbose_name_plural = verbose_name
Video 视频 model
设计
class Video(BaseModel):
lesson = models.ForeignKey(Lesson, verbose_name="章节", on_delete=models.CASCADE)
name = models.CharField(max_length=100, verbose_name=u"视频名")
learn_times = models.IntegerField(default=0, verbose_name=u"学习时长(分钟数)")
url = models.CharField(max_length=200, default="", verbose_name=u"访问地址")
class Meta:
verbose_name = "视频"
verbose_name_plural = verbose_name
CourseResource 课程资源 model
设计
# 课程资源
class CourseResource(BaseModel):
course = models.ForeignKey(Course, on_delete=models.CASCADE, verbose_name="课程")
name = models.CharField(max_length=100, verbose_name=u"名称")
file = models.FileField(upload_to="course/resourse/%Y/%m", verbose_name="下载地址", max_length=200)
class Meta:
verbose_name = "课程资源"
verbose_name_plural = verbose_name
7-7 课程机构相关的表结构设计 (12:52)
# 城市
class City(BaseModel):
name = models.CharField(max_length=20, verbose_name=u"城市")
desc = models.CharField(max_length=200, verbose_name=u"描述")
class Meta:
verbose_name = "城市"
verbose_name_plural = verbose_name
# 课程机构
class CourseOrg(BaseModel):
name = models.CharField(max_length=50, verbose_name="机构名称")
desc = models.TextField(verbose_name="描述")
tag = models.CharField(default="全国知名", max_length=10, verbose_name="机构标签")
category = models.CharField(default="pxjg", verbose_name=u"机构类别", max_length=4,
choices=(("pxjg", "培训机构"), ("gr", "个人"), ("gx", "高校")))
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"logo", max_length=100)
address = models.CharField(max_length=150, verbose_name=u"机构地址")
students = models.IntegerField(default=0, verbose_name=u"学习人数")
course_nums = models.IntegerField(default=0, verbose_name=u"课程数")
city = models.ForeignKey(City, on_delete=models.CASCADE, verbose_name=u"所在城市")
class Meta:
verbose_name = "课程机构"
verbose_name_plural = verbose_name
# 课程讲师
class Teacher(BaseModel):
org = models.ForeignKey(CourseOrg, on_delete=models.CASCADE, verbose_name="所属机构")
name = models.CharField(max_length=50, verbose_name=u"教师名")
work_years = models.IntegerField(default=0, verbose_name="工作年限")
work_company = models.CharField(max_length=50, verbose_name="就职公司")
work_position = models.CharField(max_length=50, verbose_name="公司职位")
points = models.CharField(max_length=50, verbose_name="教学特点")
click_nums = models.IntegerField(default=0, verbose_name="点击数")
fav_nums = models.IntegerField(default=0, verbose_name="收藏数")
age = models.IntegerField(default=18, verbose_name="年龄")
image = models.ImageField(upload_to="teacher/%Y/%m", verbose_name="头像", max_length=100)
class Meta:
verbose_name = "讲师"
verbose_name_plural = verbose_name
此时,回到 课程
course/models.py
中, 给 课程信息Course
加上讲师的外键. 因为每个课程都有一个讲师.
from apps.organization.models import Teacher
...
class Course(BaseModel):
teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE, verbose_name="讲师")
...
7-8 operations相关表结构设计 (16:32)
from django.db import models
from apps.users.models import BaseModel
from django.contrib.auth import get_user_model
from apps.course.models import Course
UserProfile = get_user_model()
# 用户咨询
class UserAsk(BaseModel):
name = models.CharField(max_length=20, verbose_name="姓名")
mobile = models.CharField(max_length=11, verbose_name="手机")
course_name = models.CharField(max_length=50, verbose_name="课程名")
class Meta:
verbose_name = "用户咨询"
verbose_name_plural = verbose_name
# 课程评论
class CourseComments(BaseModel):
user = models.ForeignKey(UserProfile, verbose_name="用户")
course = models.ForeignKey(Course, verbose_name="课程")
comments = models.CharField(200, verbose_name="评论内容")
class Meta:
verbose_name = "课程评论"
verbose_name_plural = verbose_name
# 用户收藏
class UserFavorite(BaseModel):
user = models.ForeignKey(UserProfile, verbose_name="用户")
fav_id = models.IntegerField(verbose_name="数据id")
fav_type = models.IntegerField(choices=((1, "课程"), (2, "课程机构"), (3, "讲师")), default=1, verbose_name="收藏类型")
class Meta:
verbose_name = "用户收藏"
verbose_name_plural = verbose_name
# 用户消息
class UserMessage(BaseModel):
user = models.ForeignKey(UserProfile, verbose_name="用户")
message = models.CharField(max_length=200, verbose_name="消息内容")
has_read = models.BooleanField(default=False, verbose_name="是否已读")
class Meta:
verbose_name = "用户消息"
verbose_name_plural = verbose_name
# 用户和课程之间的关系
class UserCourse(BaseModel):
user = models.ForeignKey(UserProfile, verbose_name="用户")
course = models.ForeignKey(Course, verbose_name="课程")
class Meta:
verbose_name = "用户课程"
verbose_name_plural = verbose_name
7-9 通过migrate生成表和本章小结 (07:23)
python3 manage.py makemigrations
python3 manage.py migrate
第8章 xadmin快速搭建后台管理系统
通过xadmin结合第4章设计的model快速的搭建一套完整的后台管理系统;本章首先介绍django admin的简单使用, 然后引出xadmin,在安装xadmin之后将model注册到xadmin中, 最后完成xadmin的全局配置
8-1 通过django的admin快速搭建后台管理系统 (25:19)
- 权限管理
- 少前端样式
- 快速开发
创建一个超级用户
python3 manage.py createsuperuser
http://127.0.0.1:8000/admin/
进入后台之后, 将语言设置为中文
在settings.py
中 将LANGUAGE_CODE = 'en-us'
修改为LANGUAGE_CODE = 'zh-hans'
再把USE_TZ = True
修改为USE_TZ = False
修改时区TIME_ZONE = 'Asia/Shanghai'
把我们自己写的
models
注册到后台中, 在users
下面的admin.py
中
from django.contrib import admin
from apps.users.models import UserProfile
class UserProfileAdmin(admin.ModelAdmin):
pass
admin.site.register(UserProfile, UserProfileAdmin)
后台如下显示
把上面的红字部分变成中文,需要再
users
下面的apps.py
中
from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'apps.users'
verbose_name = "用户"
在后台自己添加管理员的时候, 再登录,发现登录不上,原因是,我们自己添加的管理员的密码,数据库中是没有加密的.
为此, 在users
下面的admin.py
中导入from django.contrib.auth.admin import UserAdmin
, 再关联admin.site.register(UserProfile, UserAdmin)
. 代码如下
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from apps.users.models import UserProfile
class UserProfileAdmin(admin.ModelAdmin):
pass
admin.site.register(UserProfile, UserAdmin)
8-2 更加强大的后台管理系统-xadmin的配置 (16:55)
把准备好的
xadmin
拷贝到目录下.
安装xadmin
步骤
- 下载xadmin源码
- 在settings的INSTALLED_APPS中添加
crispy_forms 和 xadmin
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'apps.course.apps.CourseConfig',
'apps.users.apps.UsersConfig',
'apps.operation.apps.OperationConfig',
'apps.organization.apps.OrganizationConfig',
'crispy_forms',
'xadmin.apps.XAdminConfig',
]
- 安装xadmin的依赖包
我们再
xadmin
文件夹下有一个requirements.txt
文件夹, 可以进入虚拟环境,然后进入目录,运行pip install -r requirements.txt
来批量安装插件.
- 通过migrate生成xadmin需要的表
在之前, 先复制
DjangoUeditor
按照要求来操作. 最后配置变量没有做,但是没有报错了.
python3 manage.py makemigrations xadmin
同步xadmin
python3 manage.py migrate
- 配置一个访问地址
settings.py
from django.contrib import admin
from django.urls import path
import xadmin
urlpatterns = [
path('admin/', admin.site.urls),
path('xadmin/', xadmin.site.urls),
]
http://127.0.0.1:8000/xadmin/
8-3 解决xadmin新建用户出现手机号码重复的问题 (07:36)
在 users.py
下面的 models.py
中 mobile = models.CharField(max_length=11, verbose_name="手机号码")
把 unique=True,
去掉.
密码验证的配置, 在
settings.py
中的AUTH_PASSWORD_VALIDATORS
里面的四个. 可以注释掉. 之后重启服务.
AUTH_PASSWORD_VALIDATORS = [
# {
# 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
# },
# {
# 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
# },
# {
# 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
# },
# {
# 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
# },
]
8-4 xadmin快速配置列表、搜索、过滤等功能 (16:52)
定义
xadmin.py
import xadmin
from apps.organization.models import Teacher, CourseOrg, City
class TeacherAdmin(object):
pass
class CourseOrgAdmin(object):
pass
class CityAdmin(object):
pass
xadmin.site.register(Teacher, TeacherAdmin)
xadmin.site.register(CourseOrg, CourseOrgAdmin)
xadmin.site.register(City, CityAdmin)
在城市信息中, 添加了1条数据,如下所示,这种样式
City object(1)
需要修改
如上,需要修改,是修改
models.py
下面的__str__
函数.
# 城市
class City(BaseModel):
name = models.CharField(max_length=20, verbose_name=u"城市")
desc = models.CharField(max_length=200, verbose_name=u"描述")
class Meta:
verbose_name = "城市"
verbose_name_plural = verbose_name
def __str__(self):
return self.name
修改好之后, 页面如下显示
1. 配置列表显示项目
在
xadmin.py
中
class CityAdmin(object):
list_display = ["id", "name", "desc"]
页面如下显示
2. 配置搜索栏
class CityAdmin(object):
list_display = ["id", "name", "desc"]
search_fields = ["name", "desc"]
如上, 搜索的话,会搜索我们配置好的
name
和desc
3. 配置 过滤器
class CityAdmin(object):
list_display = ["id", "name", "desc"] # 列表显示项
search_fields = ["name", "desc"] # 模糊搜索的字段
list_filter = ["name", "desc", "add_time"] # 过滤字段
4. 直接编辑
class CityAdmin(object):
list_display = ["id", "name", "desc"] # 列表显示项
search_fields = ["name", "desc"] # 模糊搜索的字段
list_filter = ["name", "desc", "add_time"] # 过滤字段
list_editable = ["name", "desc"] # 直接编辑
5. 在外键上面加上过滤条件 course__name
class LessonAdmin(object):
list_display = ["course", "name", "add_time"] # 列表显示项
search_fields = ["course", "name"] # 模糊搜索的字段
list_filter = ["course__name", "name", "add_time"] # 过滤字段
8-5 快速注册model到xadmin中 (18:40)
8-6 xadmin全局配置和本章总结 (08:48)
1. 配置左上角的文件,和右下角的文字
在任意的
xadmin.py
文件中, 编写配置, 再绑定, 下面的GlobalSettings
名字自己取得, 最后绑定上去,所以叫什么名字都可以.
class GlobalSettings(object):
site_title = "慕学后台管理系统"
site_footer = "慕课网"
xadmin.site.register(xadmin.views.CommAdminView, GlobalSettings)
2. 配置后台多皮肤
class BaseSettings(object):
enable_themes = True
use_bootswatch = True
xadmin.site.register(xadmin.views.BaseAdminView, BaseSettings)
3. 左侧栏木树效果 和 第一个效果在一起配置
class GlobalSettings(object):
site_title = "慕学后台管理系统"
site_footer = "慕课网"
menu_style = "accordion"
xadmin.site.register(xadmin.views.CommAdminView, GlobalSettings)
第9章 登录和注册功能开发(短信动态验证码登录)
完成用户注册相关的功能, 包括登录、注册等功能, 本章会深入session和cookie的机制以及通过django form对表单进行验证。注册和验证码登录环境会通过图片验证码防止网络攻击
9-1 配置首页和登录页面 (22:55)
1. 配置首页
在
settings.py
中 配置
from django.views.generic import TemplateView
import xadmin
urlpatterns = [
# 下面的方法直接指定 模板.
path('', TemplateView.as_view(template_name="index.html")),
2. 配置登录页
下面的TemplateView
只能简单的返回html页面,没有办法编写逻辑.
path('login/', TemplateView.as_view(template_name="login.html"), name="login"),
html 中写法, 2种写法都可以
<a style="color:white" class="fr loginbtn" href="/login/">登录</a>
<a style="color:white" class="fr loginbtn" href="{% url 'login' %}">登录</a>
href="{% url 'login' %}"
这里的login
就是url
中配置的name
9-2 通过django内置的login完成登录 (27:03)
- CBV (class base view) 有利于代码重用,利于维护,后续使用这种方式
- FBV(function base view)
1. 在 Users 下面的 views.py 中, 代码如下
from django.shortcuts import render
from django.views.generic.base import View
# Create your views here.
class LoginView(View):
# 处理get请求
def get(self, request, *args, **kwargs):
return render(request, 'login.html')
# 处理post请求
def post(self, request, *args, **kwargs):
pass
2. 在 settins.py 中
from apps.users.views import LoginView
urlpatterns = [
...
path('login/', LoginView.as_view(), name="login"),
...
]
3. 重定向
from django.http import HttpResponseRedirect
from django.urls import reverse
...
# 登录成功之后应该怎么返回页面, 跳转到首页
return HttpResponseRedirect(reverse("index"))
如上
index
是urls.py
中定义路径时候的name
4. 内置方法验证用户是否存在
from django.contrib.auth import authenticate, login # 通过用户名和密码查询用户是否存在
# 通过用户名和密码查询用户是否存在
user = authenticate(username=user_name, password=password)
if user is not None:
# 查询到用户
login(request, user)
# 登录成功之后应该怎么返回页面, 跳转到首页
return HttpResponseRedirect(reverse("index"))
else:
# 未查询到用户
return render(request, "login.html", {"msg": "用户名或密码错误!"})
9-3 登录成功之后的思考 (10:15)
1. 根据内置方法, 模板中判断用户是否登录
{% if request.user.is_authenticated %}
<div>
已经登录
</div>
{% else %}
<div>未登录</div>
{% endif %}
9-4 通过form表单对登录框进行验证 (24:49)
- 在页面上留一个
HTML
,<div>{{ msg }}</div>
- 在
逻辑
中,return render(request, "login.html", {"msg": "请输入密码"})
, 这样就可以显示对应的消息信息.
# 数据验证
if not user_name:
return render(request, "login.html", {"msg": "请输入用户名"})
if not password:
return render(request, "login.html", {"msg": "请输入密码"})
if len(password) < 3:
return render(request, "login.html", {"msg": "密码格式不正确"})
简化上面代码
- 在
users
目录下, 新建1个forms.py
文件.
- 代码 如下
from django import forms
class LoginForm(forms.Form):
# 名称要和 表单提交时字段名称一致
username = forms.CharField(required=True, min_length=2)
password = forms.CharField(required=True, min_length=3)
views.py
中调用
from apps.users.forms import LoginForm # 表单验证类
# 处理post请求
def post(self, request, *args, **kwargs):
# 表单验证
login_form = LoginForm(request.POST)
if login_form.is_valid():
//... 成功验证后的代码
else:
//... 验证失败后的代码
form
除了做验证,还可以做一些数据处理,例如, 从form中获取值
# 数据的获取, 从form中获取.
user_name = login_form.cleaned_data['username']
password = login_form.cleaned_data['password']
- 目前是,点击表单,如果错误,则表单之前填入的数据都没有了,可以用
form
来完善这个问题.
return render(request, "login.html", {"login_form": login_form})
...
模板中
value="{{ login_form.username.value }}"
9-5 退出登录接口开发 (09:55)
- 判断用户是否登录
request.user.is_authenticated
, 登录之后,直接跳转到 首页.
# 判断用户是否登录
if request.user.is_authenticated:
# 登录之后直接跳转到首页
return HttpResponseRedirect(reverse("index"))
return render(request, 'login.html')
- 退出逻辑
django.contrib.auth
下面的logout
from django.contrib.auth import logout # 通过用户名和密码查询用户是否存在
...
# 退出方法
class LogoutView(View):
# 处理get请求
def get(self, request, *args, **kwargs):
logout(request)
# 退出之后重定向到首页
return HttpResponseRedirect(reverse("index"))
urls.py
中配置url
from apps.users.views import LogoutView
...
urlpatterns = [
...
path('logout/', LogoutView.as_view(), name="logout"),
...
]
- 配置到 模板中
<a class="fr" href="{% url 'logout' %}">退出</a>
9-6 通过云片网发送短信验证码 (20:32)
云片网
https://www.yunpian.com
- 在
apps
下新建一个 python文件夹utils
, 再建立1个YunPian.py
import requests
def send_simgle_sms(apikey, code, mobile):
# 发送单条短信
url = "https://sms.yunpian.com/v2/sms/single_send.json"
text = "[测试]您的验证码是{}.如非本人操作,请忽略本短信.".format(code)
res = requests.post(url, data={
"apikey": apikey,
"mobile": mobile,
"text": text
})
return res
if __name__ == "__main__":
res = send_simgle_sms("d4jiljisdf323slidjflw", "123456", "18812345678")
import json
res_json = json.loads(res.text)
code = res_json["code"]
msg = res_json["msg"]
if code == 0:
print("发送成功")
else:
print("发送失败:{}".format(msg))
print(res.text)
9-7 通过django-captcha-simple显示图片验证码 (13:52)
- 文档
https://django-simple-captcha.readthedocs.io/en/latest/usage.html#installation
- 虚拟环境中安装
pip install django-simple-captcha
settings.py
中加入到INSTALLED_APPS
INSTALLED_APPS = [
...
'DjangoUeditor',
'captcha'
]
python3 manage.py migrate
- 配置
urls.py
from django.conf.urls import url, include
...
path('captcha/', include('captcha.urls')),
...
- 安装依赖包
apt-get -y install libz-dev libjpeg-dev libfreetype6-dev python-dev
安装过可不装 - 在
users
文件夹下面的forms.py
中
from captcha.fields import CaptchaField
class DynamicLoginForm(forms.Form):
captcha = CaptchaField()
- 在
users
文件夹下面的views.py
中
from apps.users.forms import LoginForm, DynamicLoginForm
...
# Create your views here.
class LoginView(View):
# 处理get请求
def get(self, request, *args, **kwargs):
# 判断用户是否登录
if request.user.is_authenticated:
# 登录之后直接跳转到首页
return HttpResponseRedirect(reverse("index"))
login_form = DynamicLoginForm()
return render(request, 'login.html',
{
'login_form': login_form
})
- 在模板html中, 页面如下显示
<div class="form-group marb20 blur" id="jsRefreshCode">
{{ login_form.captcha }}
</div>
9-8 图片验证码是如何显示在前端页面中的 (08:08)
9-9 ajax方式完成短信验证码的发送 - 1 (15:30)
- 让某一个 url 不验证 csrf, 在
urls.py
中如下
from django.views.decorators.csrf import csrf_exempt
urlpatterns = [
...
# 短信验证接口
path('send_sms/', csrf_exempt(SendSmsView.as_view()), name="send_sms"),
...
]
- 把需要的配置都写在
settins.py
中, 之后哪里用到就引用,然后调用
# 云片网相关设置
yp_apikey = "sdfsfasdfsd"
# 调用
from Mxonline.settings import yp_apikey
# print(yp_apikey)
- 把 dict 转成 json 传递给浏览器
from django.http import JsonResponse
...
return JsonResponse({})
9-10 ajax方式完成短信验证码的发送 - 2 (16:27)
9-11 通过redis记录发送的验证码 (20:08)
- redis k-v 数据库,出入值的时候,可以设定一个时间,如果过期了,就会被 redis自动清理掉
- 安装
redis
ubuntu
sudo apt-get install redis-server
sudo apt-get install redis-cli
mac
brew install redis@3.2
配置环境变量 .bash_profile
> # redis环境变量
> export PATH=$PATH:/usr/local/opt/redis@3.2/bin
启动
brew services start redis@3.2
连接redis-cli
退出quit
常用的操作
flushdb
清空数据
keys *
查看key
set "mobile" "18888888888"
把 mobile 设置为 18888888888
get "mobile"
获取 mobile的值
安装 python redis 的驱动
- https://github.com/andymccurdy/redis-py 安装文档
pip install redis
- 在目录下建立一个
python
目录, 新建redis_test.py
文件
import redis
r = redis.Redis(host='localhost', port=6379, db=0, charset='utf8', decode_responses=True)
r.set("mobile", "123")
r.expire("mobile", 1) # 1秒之后过期
# 延迟1秒
import time
time.sleep(1)
print(r.get("mobile")) # None
- 如上,
r.expire("mobile", 1) # 1秒之后过期
这一条是设置过期时间, 过了时间就到期了.
项目中使用 redis
- 在
settings.py
配置文件中, 写入redis
的配置
# redis相关配置
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
- 在项目中
import redis
from MxOnline.settings import REDIS_HOST,REDIS_PORT
...
r = redis.Redis(host=REDIS_HOST', port=REDIS_PORT, db=0, charset='utf8', decode_responses=True)
...
r.set(str(mobile), code)
r.expire(str(mobile), 60*5) # 设置验证码五分钟过期
9-12 手机验证码动态登录 - 1 (20:24)
编写一个 view 的几个步骤
- 编写
view
代码 - 配置
url
- 修改
html
页面中相关的地址
forms.py中对某一个字段进行验证
clean_字段名
class DynamicLoginPostForm(forms.Form):
...
code = forms.CharField(required=True, min_length=4, max_length=4)
...
def clean_code(self):
pass
9-13 手机验证码动态登录 - 2 (17:38)
9-14 手机注册功能 - 1 (18:54)
- 配置
urls.py
from apps.users.views import RegisterView
...
# 注册页面
path('register/', RegisterView.as_view(), name="register"),
- 编写
view
class RegisterView(View):
def get(self, request, *args, **kwargs):
return render(request, "register.html")
def post(self, request, *args, **kwargs):
return render(request, "register.html")
- 编写一个
form
class RegisterGetForms(forms.Form):
captcha = CaptchaField()
- 用
form
改写view
class RegisterView(View):
def get(self, request, *args, **kwargs):
register_get_form = RegisterGetForms()
return render(request, "register.html", {
"register_get_form": register_get_form
})
def post(self, request, *args, **kwargs):
return render(request, "register.html")
- 改写
html
<div> {{register_get_form.captcha}} </div>
9-15 手机注册功能 - 2 (12:25)
9-16 cookie和session的登录原理和区别 (24:04)
第10章 课程机构相关功能开发
完成课程机构的相关功能, 本章会开始django的templates模板继承机制实现模板的重用。 本章包括分页、筛选、收藏等功能, 会讲到如何通过modelform对表单进行验证和保存。
10-1 使用template的static重新引入静态文件 (10:29)
10-2 通过django的template继承机制重构html页面 (22:43)
10-3 显示课程机构列表页数据 - 1 (15:31)
10-4 显示课程机构列表页数据 - 2 (12:45)
10-5 课程机构经典课程展示- 通过model反向去外键关联数据 (22:12)
10-6 课程机构分页 (19:17)
10-7 课程机构的筛选 (20:59)
10-8 通过order_by对课程机构排序 (06:08)
10-9 授课机构排名 - 通过forloop显示索引 (06:36)
10-10 通过url的include机制重新设计url (09:20)
10-11 通过modelform完成用户咨询提交…1 (27:21)
10-12 课程机构详情页 (20:43)
10-13 课程机构详情页2 (15:48)
10-14 机构讲师列表 (09:32)
10-15 机构课程和机构介绍页面开发 (16:12)
10-16 课程机构收藏 - 1 (19:16)
10-17 课程机构收藏 - 2 (17:13)
第11章 课程相关功能开发
11-1 课程列表页开发 - 1 (14:27)
11-2 课程列表页开发 - 2 (11:25)
11-3 热门课程推荐 (12:00)
11-4 课程详情页面显示 (22:46)
11-5 课程详情页的收藏和相关课程推荐 - 1 (19:08)
11-6 课程详情页的收藏和相关课程推荐 - 2 (10:44)
11-7 课程章节信息展示 (20:15)
11-8 如何控制一个view必须登录之后才能访问 (16:55)
11-9 学过该课程的同学还学习过的课程 (09:27)
11-10 课程评论页面开发 - 1 (14:49)
11-11 课程评论页面开发 - 2 (14:16)
11-12 视频播放 (21:51)
第12章 讲师相关功能开发
实现授课讲师的列表页和详情页讲师信息的展示
12-1 讲师列表页开发 (23:16)
12-2 讲师详情页面开发 (20:55)
第13章 个人中心相关功能开发
个人中心功能包括个人信息的展示和修改、 头像修改、密码修改、手机号码修改需要通过短信验证才能修改。 用户学习的课程展示、 用户的收藏展示以及删除收藏功能,最后是用户的个人消息展示
13-1 个人信息显示 (25:35)
13-2 通过django的modelform处理头像修改 (19:29)
13-3 修改个人信息 (09:53)
13-4 修改密码 (19:35)
13-5 修改手机号码 (17:50)
13-6 多种方式实现我的课程页面 (21:36)
13-7 我的收藏 - 课程机构 (18:37)
13-8 我的收藏 - 授课讲师 (10:17)
13-9 我的收藏 - 公开课程 (11:46)
13-10 全局消息提示和个人消息中心 (16:19)
第14章 首页、全局搜索和全局错误页面配置
本章主要是完成首页开发和全局搜索功能的实现,最后配置系统的全局404、403和500页面
14-1 首页 -1 (16:29)
14-2 首页 - 2 (14:53)
14-3 全局搜索功能 - 副本 (16:47)
14-4 如何快速找到所有的连接并快速的配置 (17:29)
14-5 课程详情页显示学习用户 (03:20)
14-6 自定义用户验证模块 (06:57)
14-7 自定义404、500页面 (10:53)
第15章 常见web攻击
本章介绍最常见的sql注入攻击、 xss攻击和csrf攻击的原理以及防护
15-1 sql注入攻击 (17:13)
15-2 xss攻击原理及防范 (10:12)
15-3 csrf攻击与防范 (08:38)
第16章 xadmin更进阶的开发
介绍xadmin更进阶的开发, 加深对xadmin的理解, 让整个后台管理系统完成更加细节的定制, 包括自定义详情页布局、权限的配置和管理、图片的列表页显示、ueditor富文本编辑、数据的导入和导出功能、inline的多表编辑功能等大量的配置功能。
16-1 如何修改编辑页面的布局 (16:14)
16-2 django的组和权限管理配置 (13:44)
16-3 如何定义编辑页面和新增页面的表单 (04:34)
16-4 如何让讲师可以登录xadmin并过滤列表页数据 (09:32)
16-5 重载save_models方法控制保存和修改数据的逻辑 (08:40)
16-6 同一张表的不同数据使用不同的管理器进行管理 (05:50)
16-7 通过在model中定义方法将图片显示在列表页 (07:20)
16-8 配置只读字段、排除字段和默认的排序 (06:20)
16-9 通过model_icon修改model的图标 (07:26)
16-10 通过inline配置多张表的一次性编辑 (09:27)
16-11 集成ueditor富文本编辑器到xadmin中 (27:19)
16-12 数据的导入和导出配置 (12:32)
第17章 生产环境部署-阿里云
本章主要讲解 1. nginx+uwsgi完成线上生成环境的原理 2. mysql的访问权限以及端口绑定配置,以及将本地数据库直接传输到生成环境 3. nginx配置一个虚拟主机,及完成域名和ip地址的转发、 nginx的静态文件代理 4. uwsgi的配置文件的基本配置 5. 代码变更的时候实现uwsgi服务重启