复习
# django 项目目录调整
-想把app都统一放到一个文件夹下
-apps文件夹:切换到该文件夹执行 python ../../manage.py startapp app名字
-配置文件有多个:开发环境,上线环境
-settings文件夹,创建 dev.py(原来的settings) pro.py
-django项目的启动,基于配置文件(配置文件中不能乱导入,可能会报错)
-manage.py 中修改
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffy.settings.dev')
-wsgi.py 中修改
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffy.settings.pro')
-libs文件夹:第三方模块,自己写的模块
-utils文件夹:工具
-日志,全局异常,响应对象
-项目根路径 scripts 文件夹,测试脚本
-项目根路径 logs 文件夹,日志(日志要记录到文件中)
-在配置文件中,想直接注册app名字就可以使用了(前提需要配置到环境变量里)
-配置到环境变量之后就可以,所以可以直接写
-把apps路径加入到环境变量,小luffy路径也加入了
-以后在py文件中,导入模块,选择有很多---》一般以最短路径导入
-pycharm中,导入爆红,其实不错,把文件夹组成source root
# 配置日志
-配置文件(字典),放到dev.py中
-utils/logging---》
import logging
logger=getLogger('django')
-以后要用,直接导入logger对象,对象.info或者对象.error
# 处理全局异常
-drf的全局异常:统一返回格式
-写一个函数common_exception_handler(exc, context)
-exc:异常对象,context内会有request对象,view对象
-执行一下原来的
-一定要加日志:
logger.error('系统错误:请求地址是:%s,请求的试图类是:%s,错误原因是:%s' % (path, view_name,str(exc)))
-配置文件中配置
# 封装Response
-以后返回对象时,咱们使用自己封装的(简化代码)
-return APIResponse(token='')
# 前端配置
-vue create luffy_front
-app.vue ---》<router-view/>
-全局css,清除原有标签的格式---》main.js中引入
-写了一个setting.js--->后端地址---》main.js
import settings from "./assets/js/settings";
Vue.prototype.$settings=settings
-axios--->安装---》放到原型中
import axios from "axios";
Vue.prototype.$axios=axios
-使用
this.$axios.get(this.$settings.base_url+'/home/')
# 魔法方法
- __init__和__new__的区别
init:实例化类时候调用
new:创建对象时调用 在init之前执行
今日内容
1 前台使用elementui,bootstrap,jQuery
# 使用饿了么UI
-cnpm i element-ui -S
-main.js中写入
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
# bootstrap
-cnpm install bootstrap@3
-main.js中
import 'bootstrap'
import 'bootstrap/dist/css/bootstrap.min.css'
# jq
-cnpm install jquery
-在项目根路径下新建:vue.config.js
const webpack = require("webpack");
module.exports = {
configureWebpack: {
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery",
"window.$": "jquery",
Popper: ["popper.js", "default"]
})
]
}
};
-一定要重启
2 后台User模块,User表
# 补充:软件开发模式
-bbs项目:瀑布开发模式
瀑布开发模式就好比是火箭发射,工作和任务是预先计划好的,启动之后调整和返回难度很大。
-路飞项目:敏捷开发
敏捷开发模式就像是汽车驾驶,有很多条路可以到达目的地,可以随时根据导航反馈的路况进行及时调整。这个调整过程称为“开发—测量—认知”的反馈循环。
# 用户表:使用auth 的user表,自定义user表
# 咱们用了auth的user表----》扩写一些字段
# 创建一个app user
python ../../manage.py startapp user
# user app的models中写表
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
mobile = models.CharField(max_length=11, unique=True) # 唯一
# 需要pillow包的支持
icon = models.ImageField(upload_to='icon', default='icon/default.png')
class Meta:
db_table = 'luffy_user' # 如果不配它,表名是 app名字_类名小写
verbose_name = '用户表' # 在admin中显示的表名
verbose_name_plural = verbose_name # 在admin中显示的表名,不配它会加个s
def __str__(self): # print(对象) 的时候,触发它的执行
return self.username
# 在配置文件中配置
AUTH_USER_MODEL = "app名.表名"
AUTH_USER_MODEL='user.user'
# 迁移数据库
python manage.py makemigrations
python manage.py migrate
#### 注意:
-如果是空项目,之前数据库没有表,这么做完全没问题
-但如果你之前迁移过数据库,这样就不行了
-因为user表已经存在了
-第一步:删库
-第二部:删除迁移文件(所有app的迁移文件)
-第三步:删除django内置app的迁移文件(auth和admin这个app下migrations)
-第四步:从新迁移数据库
python manage.py makemigrations
python manage.py migrate
# 进行media的配置 图片的配置
##1 dev.py中
MEDIA_URL = '/media/' #luffy下的/media文件夹
MEDIA_ROOT = os.path.join(BASE_DIR, 'media') #拼接上
## 2 开路由
from django.views.static import serve
from django.conf import settings #用django的settings他会反射到自己写的MEDIA_ROOT
urlpatterns = [
#有名分组
path('media/<path:path>', serve, {'document_root': settings.MEDIA_ROOT})
]
3 前台首页设计
组件化开发 写三个组件 分别是导航栏 轮播图 尾部 写在components
Header Footer Banner
<template>
<div class="header">
<div class="slogan">
<p>老男孩IT教育 | 帮助有志向的年轻人通过努力学习获得体面的工作和生活</p>
</div>
<div class="nav">
<ul class="left-part">
<li class="logo">
<router-link to="/">
<img src="../assets/img/head-logo.svg" alt="">
</router-link>
</li>
<li class="ele">
<span @click="goPage('/free-course')" :class="{active: url_path === '/free-course'}">免费课</span>
</li>
<li class="ele">
<span @click="goPage('/actual-course')" :class="{active: url_path === '/actual-course'}">实战课</span>
</li>
<li class="ele">
<span @click="goPage('/light-course')" :class="{active: url_path === '/light-course'}">轻课</span>
</li>
</ul>
<div class="right-part">
<div>
<span>登录</span>
<span class="line">|</span>
<span>注册</span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Header",
data() {
return {
url_path: sessionStorage.url_path || '/',
}
},
methods: {
goPage(url_path) {
// 已经是当前路由就没有必要重新跳转
if (this.url_path !== url_path) {
this.$router.push(url_path);
}
sessionStorage.url_path = url_path;
},
},
created() {
sessionStorage.url_path = this.$route.path;
this.url_path = this.$route.path;
}
}
</script>
<style scoped>
.header {
background-color: white;
box-shadow: 0 0 5px 0 #aaa;
}
.header:after {
content: "";
display: block;
clear: both;
}
.slogan {
background-color: #eee;
height: 40px;
}
.slogan p {
width: 1200px;
margin: 0 auto;
color: #aaa;
font-size: 13px;
line-height: 40px;
}
.nav {
background-color: white;
user-select: none;
width: 1200px;
margin: 0 auto;
}
.nav ul {
padding: 15px 0;
float: left;
}
.nav ul:after {
clear: both;
content: '';
display: block;
}
.nav ul li {
float: left;
}
.logo {
margin-right: 20px;
}
.ele {
margin: 0 20px;
}
.ele span {
display: block;
font: 15px/36px '微软雅黑';
border-bottom: 2px solid transparent;
cursor: pointer;
}
.ele span:hover {
border-bottom-color: orange;
}
.ele span.active {
color: orange;
border-bottom-color: orange;
}
.right-part {
float: right;
}
.right-part .line {
margin: 0 10px;
}
.right-part span {
line-height: 68px;
cursor: pointer;
}
</style>
<template>
<div class="footer">
<ul>
<li>关于我们</li>
<li>联系我们</li>
<li>商务合作</li>
<li>帮助中心</li>
<li>意见反馈</li>
<li>新手指南</li>
</ul>
<p>Copyright © luffycity.com版权所有 | 京ICP备17072161号-1</p>
</div>
</template>
<script>
export default {
name: "Footer"
}
</script>
<style scoped>
.footer {
width: 100%;
height: 128px;
background: #25292e;
color: #fff;
}
.footer ul {
margin: 0 auto 16px;
padding-top: 38px;
width: 810px;
}
.footer ul li {
float: left;
width: 112px;
margin: 0 10px;
text-align: center;
font-size: 14px;
}
.footer ul::after {
content: "";
display: block;
clear: both;
}
.footer p {
text-align: center;
font-size: 12px;
}
</style>
Banner.vue
<template>
<el-carousel :interval="4000" type="card" height="400px">
<el-carousel-item v-for="item in 4" :key="item">
<!-- <h3 class="medium">{{ item }}</h3>-->
<img src="../assets/image/banner1.png" alt="">
</el-carousel-item>
</el-carousel>
</template>
<script>
export default {
name: "Banner",
data() {
},
created() {
}
}
</script>
<style scoped>
.el-carousel__item h3 {
color: #475669;
font-size: 14px;
opacity: 0.75;
line-height: 200px;
margin: 0;
}
.el-carousel__item:nth-child(2n) {
background-color: #99a9bf;
}
.el-carousel__item:nth-child(2n+1) {
background-color: #d3dce6;
}
.el-carousel__item {
height: 400px;
/*min-width: 1200px;*/
}
.el-carousel__item img {
height: 400px;
margin-left: calc(50% - 1920px / 2);
}
</style>
Home.vue
<template>
<div class="home">
<Header></Header>
<Banner></Banner>
<div class="course">
<el-row>
<el-col :span="6" v-for="(o, index) in 8" :key="o">
<el-card :body-style="{ padding: '0px' }" class="card">
<img src="https://img0.baidu.com/it/u=1396426037,2146632168&fm=253&fmt=auto&app=138&f=JPEG?w=631&h=500"
class="image">
<div style="padding: 14px;">
<span>崽种,瞅我</span>
<div class="bottom clearfix">
<time class="time">{{ currentDate }}</time>
<el-button type="text" class="button">别TM点</el-button>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<Footer></Footer>
</div>
</template>
<script>
import Header from "../components/Header";
import Footer from "../components/Footer";
import Banner from "../components/Banner";
export default {
name: 'Home',
created() {
// this.$axios.get(this.$settings.base_url + '/home/test2/').then(res => {
// console.log(res.data)
// })
},
data() {
return{
currentDate: new Date()
};
},
methods: {
},
components: {
Header, Footer, Banner
}
}
</script>
<style>
.time {
font-size: 13px;
color: #999;
}
.bottom {
margin-top: 13px;
line-height: 12px;
}
.button {
padding: 0;
float: right;
}
.image {
width: 100%;
display: block;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both
}
.course {
margin-left: 20px;
margin-right: 20px;
}
.card {
margin: 30px;
}
</style>
4 后台首页相关接口(轮播图图片的接口)
# 创建公共表 utils/nodels
from django.db import models
# 公共表
class BaseModel(models.Model):
created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
updated_time = models.DateTimeField(auto_now=True, verbose_name='最后更新时间')
is_delete = models.BooleanField(default=False, verbose_name='是否删除')
is_show = models.BooleanField(default=True, verbose_name='是否上架')
orders = models.IntegerField(verbose_name='优先级')
# 不让他在数据库创建表 ,不写这个迁移就会创建表
class Meta:
abstract = True #写了这个虚拟表不生成
#创建home下的banner表,并添加自己的字段
from django.db import models
# Create your models here.
from utils.models import BaseModel
class Banner(BaseModel):
title = models.CharField(max_length=16, unique=True, verbose_name='名称')
image = models.ImageField(upload_to='banner', verbose_name='图片')
link = models.CharField(max_length=64, verbose_name='跳转链接') # 跳转地址
info = models.TextField(verbose_name='详情') # 也可以用详情表,宽高出处
class Meta:
db_table = 'luffy_banner'
verbose_name_plural = '轮播图表'
def __str__(self):
return self.title
# 写一个方法
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from .models import Banner
from .Serializer import BannerSerializer
class BannerView(GenericViewSet,ListModelMixin):
serializer_class =BannerSerializer
queryset = Banner.objects.all().filter(is_delete=False,is_show=True)
#序列化类也要写
from .models import Banner
from rest_framework import serializers
class BannerSerializer(serializers.ModelSerializer):
class Meta:
model = Banner
fields = ['title','image','link']
#自动生成路由
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('banner', views.BannerView, 'banner')
urlpatterns += router.urls
5 跨域请求详解(重要)
# 如何保证自己的接口不被第三方调用
# 浏览器的安全策略:同源策略
-请求的url地址,必须与浏览器上的url地址处于同域上,也就是域名,端口,协议相同.
-如果不一样浏览器上就会报错,这个就是同源策略的保护,如果浏览器对javascript没有同源策略的保护,那么一些重要的机密网站将会很危险
-请求,服务的执行了,数据返回了,但是浏览器拦截掉了
# 解决同源策略导致的数据不能正常加载
# csrf 跨站请求伪造
# xss 跨站脚本攻击
# CORS:跨域资源共享---》后端技术(后端代码中加东西)---》通过它,就可以解决跨域问题
-CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能
-浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)
-如果请求满足以下两种情况,就是简单请求,否则就是非简单请求
(1) 请求方法是以下三种方法之一:
HEAD
GET
POST
(2)HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值urlencoded、form-data、text/plain
-简单请求发送一次请求,数据拿回来,但是被浏览器同源策略拦截了
-非简单请求发送两次,一次options请求,如果允许再发真正的请求,如果不允许就不发了
-通过跨域资源共享解决跨域
-简单请求解决:其实就是在响应头中加一些东西
res=HttpResponse('ok')
#请求头加参数
res['Access-Control-Allow-Origin']='*'
return res
-非简单请求的解决,也是在响应头中加东西,中间件记得注释csrf 如果用drf不用
if request.method == 'OPTIONS':
# res['Access-Control-Allow-Methods'] = 'DELETE,'
res['Access-Control-Allow-Headers'] = 'Content-Type'
-最好:写一个中间件,
from django.utils.deprecation import MiddlewareMixin
class CORSMiddle(MiddlewareMixin):
def process_response(self, request, response):
#逻辑判断
if request.method == 'OPTIONS':
response['Access-Control-Allow-Methods'] = 'DELETE,'
response['Access-Control-Allow-Headers'] = 'Content-Type'
response['Access-Control-Allow-Origin'] = '*'
return response
-配置在配置文件中 配置在中间件
MIDDLEWARE = [
'home.middleware.CORSMiddle'
]
# 使用第三方解决:
1.-安装
pip install django-cors-headers
2.-注册:添加到setting的app中
INSTALLED_APPS = (
...
'corsheaders',
...
)
-加入到中间件
'corsheaders.middleware.CorsMiddleware',
-配置文件配置
#允许所有域
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_METHODS = (
'DELETE',
'GET',
'OPTIONS',
'PATCH',
'POST',
'PUT',
'VIEW',
)
#允许请求头多的东西
CORS_ALLOW_HEADERS = (
'XMLHttpRequest',
'X_FILENAME',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
'Pragma',
'token',
)