WEB开发流程
一、需求分析
二、数据库设计
三、后台管理系统
四、全栈功能实现
五、基础功能补充
六、xadmin的进阶开发
django目录
一级目录:
项目: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实现不同的功能
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表示用户行为
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-课程管理
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-机构和教师管理
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-用户操作管理
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)
注意:添加用户注意在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 以及相关依赖包都安装完成了。
(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更新数据迁移
数据库会更新出一些新的表
2.4 源码的安装
也可以将xadmin拷贝到项目中,修改源码,提供丰富的插件
在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,内置功能丰富,强大的插件系统
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"
效果图
四、全栈功能实现
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 %}
<div class="top">
<div class="wp">
<div class="fl"><p>服务电话:<b>18582854749</b></p></div>
<!--登录后跳转-->
<div class="personal">
<dl class="user fr">
<dd>鹏鹏<img class="down fr" src="/static/images/top_down.png"/></dd>
<dt><img width="20" height="20" src="/static/media/image/2016/12/default_big_14.png"/></dt>
</dl>
<div class="userdetail">
<dl>
<dt><img width="80" height="80" src="/static/media/image/2016/12/default_big_14.png"/></dt>
<dd>
<h2>管理员</h2>
<p>root@qq.com</p>
</dd>
</dl>
<div class="btn">
<a class="personcenter fl" href="usercenter-info.html">进入个人中心</a>
<a class="fr" href="/logout/">退出</a>
</div>
</div>
</div>
</div>
</div>
{% else %}
<div class="top">
<a style="color:white" class="fr registerbtn" href="register.html">注册</a>
<a style="color:white" class="fr loginbtn" href="/login/">登录</a>
{% endif %}
login.html 如果用户信息出错,则显示错误信息
<div class="error btns login-form-tips" id="jsLoginTips">{
{ msg }}</div>
(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)
前端设置信息
#高亮
<div class="form-group marb20 {% if login_form.errors.username %}errorput{% endif %}">
#错误提示
<div class="error btns login-form-tips" id="jsLoginTips">{% for key, error in login_form.errors.items %}{
{ key }}:{
{ error }}{% endfor %}{
{ msg }}</div>
#路径返回
<form id="email_register_form" method="post" action="{% url 'register' %}" autocomplete="off">
{% csrf_token %}
#信息回填
value="{
{ register_form.email.value }}
效果图如下:
(5)cookies和sesson机制实现自动登陆
cookie在本地浏览器,sesson服务器
1.2 注册后台编写
(1)配置
url.py
url('^register/$', RegisterView.as_view(), name="register"),
index.html
<div class="top">
<a style="color:white" class="fr registerbtn" href="{% url 'register' %}">注册</a>
<a style="color:white" class="fr loginbtn" href="{% url 'login' %}">登录</a>
register.html,使用这样的方式设置路径,便于以后路径的修改
<!DOCTYPE html>
<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<active_code>.*)/$', ActiveUserView.as_view(), name="user_active"),
settings,注意授权,密码是授权码
#配置邮箱验证
EMAIL_HOST = "smtp.163.com"
EMAIL_PORT = 25
EMAIL_HOST_USER = "wenpeng_111@163.com"
EMAIL_HOST_PASSWORD = "51dk648171lm" #授权码
EMAIL_USE_TLS = False
EMAIL_FROM = "wenpeng_111@163.com" #指明发件人
在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<active_code>.*)/$', ResetView.as_view(), name="reset_pwd"),
url(r'^modify_pwd/$', ModifyPwdView.as_view(), name="modify_pwd"),
password_reset.html
<div class="wp">
<div class="resetpassword" id="resetPwdForm">
<h1>修改密码</h1>
<p>已经通过验证,请设置新密码</p>
<form id="reset_password_form" action="{% url 'modify_pwd' %}" method="post">
<ul>
<li>
<span class="">新 密 码 :</span>
<input type="password" name="password1" id="pwd" placeholder="6-2