一、后台主页模块设计
| |
| python ../../manage.py startapp home |
| |
| |
| -写一个基表BaseModel |
| -写轮播图表 |
| 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 |
迁移,别忘记注册和回到根路径
| from utils.common_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 |
二、simpleui后台管理
| 1.下载:pip install django-simpleui |
| 2.注册 'simpleui' |
| 3.国际化 |
| LANGUAGE_CODE = 'zh-hans' |
| |
| TIME_ZONE = 'Asia/Shanghai' |
| |
| USE_I18N = True |
| |
| USE_L10N = True |
| |
| USE_TZ = False |
| 4.注册超级管理员 |
| python manage.py createsuperuser |
| yzk |
| yzk12345 |
| 5.去admin.py里面注册 |
| from .models import Banner |
| |
| admin.site.register(Banner) |
| |
| |
| 6.去apps里面加入,录入数据 |
| class HomeConfig(AppConfig): |
| default_auto_field = 'django.db.models.BigAutoField' |
| name = 'home' |
| verbose_name = '首页功能' |
| |
| 7.配置文件 |
| MEDIA_URL = 'media/' |
| |
| |
| |
| |
| |
三、轮播图接口
3.1 封装自己的mixin
| from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, \ |
| RetrieveModelMixin |
| from utils.common_response import APIResponse |
| |
| |
| class CommonListModelMixin(ListModelMixin): |
| def list(self, request, *args, **kwargs): |
| res = super().list(request, *args, **kwargs) |
| return APIResponse(data=res.data) |
| |
| |
| class CommonCreateModelMixin(CreateModelMixin): |
| def create(self, request, *args, **kwargs): |
| res = super().create(request, *args, **kwargs) |
| return APIResponse(data=res.data) # {code:100,msg:成功,data:{}} |
| |
| |
| class CommonUpdateModelMixin(UpdateModelMixin): |
| def update(self, request, *args, **kwargs): |
| res = super(CommonUpdateModelMixin, self).update(request, *args, **kwargs) |
| return APIResponse(data=res.data) |
| |
| |
| class CommonRetrieveModelMixin(RetrieveModelMixin): |
| def retrieve(self, request, *args, **kwargs): |
| res = super(CommonRetrieveModelMixin, self).retrieve(request, *args, **kwargs) |
| return APIResponse(data=res.data) |
| |
| |
| class CommonDestroyModelMixin(DestroyModelMixin): |
| def destroy(self, request, *args, **kwargs): |
| res = super().destroy(request, *args, **kwargs) |
| return APIResponse(msg='删除成功') |
| |
3.2 轮播图接口
视图
| # 查询所有 轮播图接口 |
| from rest_framework.viewsets import GenericViewSet |
| from rest_framework.mixins import ListModelMixin |
| from .serialzier import BannerSerializer |
| from .models import Banner |
| from utils.common_mixin import CommonListModelMixin as ListModelMixin |
| |
| |
| class BannerView(GenericViewSet, ListModelMixin): |
| queryset = Banner.objects.all().filter(is_delete=False, is_show=True).order_by('orders') |
| serializer_class = BannerSerializer |
| |
路由
| |
| urlpatterns = [ |
| path('admin/', admin.site.urls), |
| path('api/v1/home/', include('home.urls')), |
| |
| path('media/<path:path>', serve, {'document_root': settings.MEDIA_ROOT}), |
| |
| ] |
| |
| |
| from rest_framework.routers import SimpleRouter |
| |
| router = SimpleRouter() |
| from .views import BannerView |
| |
| router.register('banner', BannerView, 'banner') |
| urlpatterns = [ |
| |
| |
| ] |
| urlpatterns += router.urls |
序列化类
| from rest_framework import serializers |
| from .models import Banner |
| |
| |
| class BannerSerializer(serializers.ModelSerializer): |
| class Meta: |
| model = Banner |
| fields = ['id', 'image', 'title', 'link'] |
3.3 自定义配置文件
| # 通过配置文件控制显示多少条轮播图 |
| # 使用步骤 |
| 1 在settings下新建:common_settings.py |
| BANNER_COUNT = 3 |
| 2 在dev.py中加入 |
| from .common_settings import * |
| 3 在轮播图接口上 |
| class BannerView(GenericViewSet, ListModelMixin): |
| queryset = Banner.objects.all().filter(is_delete=False, is_show=True).order_by('orders')[:settings.BANNER_COUNT] |
| serializer_class = BannerSerializer |
| |
四、跨域问题详解
| |
| |
| |
| 同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现 |
| |
| 请求的url地址,必须与浏览器上的url地址处于同域上:也就是[域名],[端口],[协议]相同. |
| http://127.0.0.1:8080 |
| ftf://127.0.0.1:8080 |
| |
| 比如:我在本地上的域名是127.0.0.1:8000,请求另外一个域名:127.0.0.1:8001一段数据 |
| |
| 浏览器上就会报错,个就是同源策略的保护,如果浏览器对javascript没有同源策略的保护,那么一些重要的机密网站将会很危险 |
| |
| 请求发送,服务的执行,数据也正常返回,只是被浏览器拦截了 |
| |
| |
| |
| |
| |
| 1 jsonp 跨域(不了解) |
| 2 跨域资源共享(CORS) 后端技术 |
| 3 Nginx代理 |
| |
| |
| |
| CORS需要浏览器和服务器同时支持,所有浏览器都支持该功能 |
| 只需要服务的处理即可:只需要在在响应头中加入固定的头就实现cors---》比如在响应头中加入Access-Control-Allow-Origin='*'---->get请求就没有跨域了---》但是put请求还会有 |
| |
| |
| |
| -简单请求,浏览器直接发起 |
| -非简单请求,浏览器先发送要给options预检请求,服务端允许,再发送真正的请求 |
| |
| |
| 1 请求方法是以下三种方法之一: |
| HEAD |
| GET |
| POST |
| 2 HTTP的头信息不超出以下几种字段: |
| Accept |
| Accept-Language |
| Content-Language |
| Last-Event-ID |
| Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain |
| |
| |
| |
| res['Access-Control-Allow-Headers'] = 'token' |
| res['Access-Control-Allow-Methods'] = 'DELETE' |
| res['Access-Control-Allow-Origin'] = 'http://192.168.1.252:8080' |
| |
| |
| |
| |
| |
| |
| 前端访问的后端地址,一定要准确 |
3.0 自定义中间件,解决跨域问题
| |
| from django.utils.deprecation import MiddlewareMixin |
| |
| |
| |
| class CorsMiddleware(MiddlewareMixin): |
| def process_response(self, request, response): |
| if request.method == 'OPTIONS': |
| response['Access-Control-Allow-Headers'] = 'token' |
| response['Access-Control-Allow-Methods'] = 'DELETE' |
| response['Access-Control-Allow-Origin'] = '*' |
| return response |
| |
| |
| MIDDLEWARE = [ |
| 'utils.common_mideleware.CorsMiddleware' |
| ] |
| |
| pip install django-cors-headers |
| |
| INSTALLED_APPS = ( |
| ... |
| 'corsheaders', |
| ... |
| ) |
| |
| MIDDLEWARE = [ |
| ... |
| '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' |
| ) |
| |
| class CorsMiddleware(MiddlewareMixin): |
| def process_response(self, request, response): |
| if ( |
| not conf.CORS_ALLOW_ALL_ORIGINS |
| and not self.origin_found_in_white_lists(origin, url) |
| and not self.check_signal(request) |
| ): |
| return response |
| if conf.CORS_ALLOW_ALL_ORIGINS and not conf.CORS_ALLOW_CREDENTIALS: |
| response[ACCESS_CONTROL_ALLOW_ORIGIN] = "*" |
| else: |
| response[ACCESS_CONTROL_ALLOW_ORIGIN] = origin |
| if request.method == "OPTIONS": |
| response[ACCESS_CONTROL_ALLOW_HEADERS] = ", ".join(conf.CORS_ALLOW_HEADERS) |
| response[ACCESS_CONTROL_ALLOW_METHODS] = ", ".join(conf.CORS_ALLOW_METHODS) |
| return response |
4 前台主页功能
4.1 Banner.vue
| <template> |
| <div> |
| <el-carousel height="400px"> |
| <el-carousel-item v-for="item in banner_list" :key="item"> |
| <!-- router-link只能跳内部,不能跳外部--> |
| |
| <router-link :to="item.link" v-if="item.link.indexOf('http:')<0"> |
| <img :src="item.image" :alt="item.title"> |
| </router-link> |
| <a :href="item.link" v-else> |
| <img :src="item.image" :alt="item.title"> |
| </a> |
| |
| </el-carousel-item> |
| </el-carousel> |
| </div> |
| </template> |
| |
| <script> |
| export default { |
| name: "Banner", |
| data() { |
| return {banner_list: []} |
| }, |
| created() { |
| this.$axios.get(`${this.$settings.BASE_URL}home/banner/`).then(res => { |
| this.banner_list = res.data.data |
| }) |
| } |
| } |
| </script> |
| |
| <style scoped> |
| .el-carousel__item h3 { |
| color: #475669; |
| font-size: 18px; |
| opacity: 0.75; |
| line-height: 300px; |
| margin: 0; |
| } |
| |
| |
| .el-carousel__item { |
| height: 400px; |
| min-width: 1200px; |
| } |
| |
| .el-carousel__item img { |
| height: 400px; |
| margin-left: calc(50% - 1920px / 2); |
| } |
| </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> |
| |
| <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> |
| |
4.4 HomeView.vue
| <template> |
| <div class="home"> |
| <Header></Header> |
| <Banner></Banner> |
| <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 Banner from "@/components/Banner"; |
| import Footer from '@/components/Footer' |
| import Header from "@/components/Header"; |
| |
| export default { |
| name: 'HomeView', |
| // created() { |
| // |
| // this.$axios.get('http://127.0.0.1:8000/api/v1/home/test/', { |
| // headers: { |
| // token: 'sdfafasdf' |
| // } |
| // |
| // }).then(res => { |
| // console.log(res.data.data) |
| // }) |
| // } |
| |
| components: { |
| Header, Footer, Banner |
| } |
| |
| } |
| </script> |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步