#后端
django_rest_form的详细过程
文件配置
安装restframework
pip install djangorestframework
安装filter(在接口搜素文件的时候会用到)
pip install django-filter
安装jwt
pip install djangorestframework-jwt
安装pillow(上传照片时会用到)
pip install pillow
设置跨域请求
pip install django-cors-header
setting的配置(setting .py)
注册app
在INSTALLED_APPS添加新建的应用,如restframework
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'apps.user.apps.UserConfig',
'apps.task.apps.TaskConfig',
'rest_framework',
'corsheaders',
'rest_framework.authtoken',#jwt认证的时候要加上
'django_filters',
]
自定义设置规则(rest_framework,jwt)
'DEFAULT_FILTER_BACKENDS': (
'django_filters.rest_framework.DjangoFilterBackend',
),
'EXCEPTION_HANDLER': 'common.customexception.custom_exception_handler',
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
"DEFAULT_SCHEMA_CLASS": "rest_framework.schemas.coreapi.AutoSchema"
# 'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),
}
JWT_AUTH = {
# 自定义jwt异常
# 'JWT_RESPONSE_PAYLOAD_ERROR_HANDLER': 'common.jwt_exception.jwt_response_payload_error_handler',
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), # Token 过期时间为一周
'JWT_AUTH_HEADER_PREFIX': 'JWT', # Token的头为:JWT XXXXXXXXXXXXXXXXXXXXXX
'JWT_ALLOW_REFRESH': True,
# 自定义返回认证信息
'JWT_RESPONSE_PAYLOAD_HANDLER': 'common.jwt_utils.jwt_response_payload_handler',
'JWT_RESPONSE_PAYLOAD_ERROR_HANDLER': 'Users.views.jwt_response_payload_error_handler',
}
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',#用在跨域请求上面,要发布放在第一个位置
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
设置默认资源地址(static(用于静态资源的存放),media(用于图片,文件上传))
STATIC_URL = '/static/'
MEDIA_URL = '/media/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
# STATICFILES_DIRS = [os.path.join(BASE_DIR, 'media')]
# STATIC_ROOT = os.path.join(BASE_DIR, 'static')
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
设置默认用户列表(自定义用户列表)
AUTH_USER_MODEL = "user.MyUser"
设置跨域请求的配置
CORS_ALLOW_CREDENTIALS = True
路由的配置(urls.py)
路由分配(保证每一个接口都专门化)
path('admin/', admin.site.urls),
path('user/', include("apps.user.user_urls")),#用于用户相关的请求
path('task/', include("apps.task.task_urls")),#用于任务相关的请求
path('docs/', include_docs_urls(title="校园任务接口平台")),#接口文档
设置static,media,等资源分配的路由地址
from django.views.static import serve
from django.conf import settings
re_path('static/(?P<path>.*)', serve, {"document_root": settings.STATIC_ROOT}),
re_path('media/(?P<path>.*)', serve, {"document_root": settings.MEDIA_ROOT}),
admin文件的配置(admin.py)
from django.contrib import admin
from .models import *
# Register your models here.#注册用户自己的用户模型,方便在后台管理数据
@admin.register(MyUser)
class admin_MyUser(admin.ModelAdmin):
list_display = ['username']
序列化文件的配置(serializer.py)
from rest_framework import serializers
from .models import *
class serializer_mark(serializers.ModelSerializer):
#可以在这里定义几个字段(在数据模型中的,方便管理,如设置只读)
xxxx=selializer.textfield(readonly=true)
class Meta:
model = mark
fields = ('mark', "id",)
# exclude = ("create_time", 'update_time', 'click_count')
#fields = "__all__"
数据表的配置(models.py)
用户列表
from django.contrib.auth.models import AbstractUser#用于自定义用户表,或者说想要额外添加信息的话就可以用则会个类
from django.db import models
class MyUser(AbstractUser):
username = models.CharField('姓名', blank=True, max_length=50, unique=True)
mobile = models.CharField('手机号码', max_length=11, default="", blank=True)
wx = models.CharField('微信号', max_length=20, default="", blank=True)
qq = models.CharField('qq号码', max_length=20, default="", blank=True)
mibao = models.CharField('密保问题', max_length=200, default="", blank=True)
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
def __str__(self):
return self.username
定义需要的数据库模型(定义类)
class MyUser(AbstractUser):
注意要设置成是否为空
username = models.CharField('姓名', blank=True, max_length=50, unique=True)
注意外键关系(要关联那些数据模型)
class task_details(models.Model):
title = models.CharField(max_length=30, null=True, blank=True, verbose_name="任务标题")
catgory = models.ForeignKey(mark, on_delete=models.CASCADE, null=True, blank=True, verbose_name="任务分类")
introduction = models.TextField(max_length=500, blank=True, null=True, verbose_name="任务介绍")
click_count = models.IntegerField(verbose_name="任务点击数量", blank=True, null=True)
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
user_id = models.ForeignKey(MyUser, on_delete=models.CASCADE, verbose_name="所属用户", default=5)#关联了用户自定义的用户列表
视图函数(views.py)
主要使用viewset.modelviewset类(如果额外需求可以重写他的函数)
class view_all_task(CustomModelViewSet):#这个是继承我们自己定义的(改装默认的类)
queryset = task_details.objects.all()#填写models中的数据模型
serializer_class = serialiser_task_details#写序列化类中的内容
pagination_class = MyPage#写自己的自定义分页类
#tips:可以将自定义(继承子上一个类)的文件放到单数一个文件,方便以后管理
定义自定义返回类(在viewset.modelviewset的基础下)
from rest_framework import status
from rest_framework import viewsets
from common.customresponse import CustomResponse
class CustomModelViewSet(viewsets.ModelViewSet):
# CreateModelMixin->create
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return CustomResponse(data=serializer.data, code=201, msg="OK", status=status.HTTP_201_CREATED, headers=headers)
# ListModelMixin->list
def list(self, request, *args, **kwargs):
queryset = self.get_queryset()
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return CustomResponse(data=serializer.data, code=200, msg="OK", status=status.HTTP_200_OK)
# RetrieveModelMixin->retrieve
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return CustomResponse(data=serializer.data, code=200, msg="OK", status=status.HTTP_200_OK)
# UpdateModelMixin->update
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {}
return CustomResponse(data=serializer.data, code=200, msg="OK", status=status.HTTP_200_OK)
# DestroyModelMixin->destroy
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return CustomResponse(data=[], code=204, msg="OK", status=status.HTTP_204_NO_CONTENT)
from rest_framework.response import Response
from rest_framework.serializers import Serializer
class CustomResponse(Response):
def __init__(self, data=None, code=None, msg=None,
status=None,
template_name=None, headers=None,
exception=False, content_type=None, **kwargs):
super().__init__(None, status=status)
if isinstance(data, Serializer):
msg = (
'You passed a Serializer instance as data, but '
'probably meant to pass serialized `.data` or '
'`.error`. representation.'
)
raise AssertionError(msg)
self.data = {'code': code, 'msg': msg, 'data': data}
self.data.update(kwargs)
self.template_name = template_name
self.exception = exception
self.content_type = content_type
if headers:
for name, value in headers.items():
self[name] = value
自定义异常类
from rest_framework.views import exception_handler
def custom_exception_handler(exc, context):
response = exception_handler(exc, context)
if response is not None:
response.data.clear()
#组装code、msg、data
response.data['code'] = response.status_code
if response.status_code == 405:
response.data["msg"]="请求不允许"
elif response.status_code == 401:
response.data["msg"] = "认证未通过"
elif response.status_code == 403:
response.data["msg"] = "禁止访问"
elif response.status_code == 404:
response.data["msg"] = "未找到文件"
elif response.status_code >= 500:
response.data["msg"]="服务器异常"
else:
response.data["msg"] = "其他未知错误"
response.data['data'] = []
return response
自定义分页类
from rest_framework import status
from rest_framework.pagination import PageNumberPagination
from common.customresponse import CustomResponse
class MyPage(PageNumberPagination):
page_size = 8 #每页显示数量
max_page_size = 50 #每页最大显示数量。
page_size_query_param = 'size' #每页数量的参数名称
page_query_param = 'page' #页码的参数名称
def get_paginated_response(self, data):
return CustomResponse(data=data,code=200,msg="OK",status=status.HTTP_200_OK, count=self.page.paginator.count,
next=self.get_next_link(),previous=self.get_previous_link())
自定义用户列表
取代django默认的用户列表(别忘了在setting里面设置默认的用户列表)
from django.contrib.auth.models import AbstractUser
from django.db import models
###在使用之前要删除以前的数据库,生成的sql语句,只能添加一些额外的个人信息(在django默认的用户列表的基础上)
class MyUser(AbstractUser):
username = models.CharField('姓名', blank=True, max_length=50, unique=True)
mobile = models.CharField('手机号码', max_length=11, default="", blank=True)
wx = models.CharField('微信号', max_length=20, default="", blank=True)
qq = models.CharField('qq号码', max_length=20, default="", blank=True)
mibao = models.CharField('密保问题', max_length=200, default="", blank=True)
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
def __str__(self):
return self.username
用户权限
目前使用的使用的是jwt认证方式
自定义jwt消息返回(在login的时候)
def jwt_response_payload_handler(token, user=None, request=None):
return {
"token": token,
'id': user.id,
'username': user.username,
'email': user.email,
'is_active': user.is_active,
}
实现用户登录
from django.urls import path
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
path('login', obtain_jwt_token),
]
实现用户注册
########url文件中的配置
from django.urls import path
from rest_framework_jwt.views import obtain_jwt_token
from .views import *
user_list = MyUserViewSet.as_view({
'get': 'retrieve',
'post': 'create',
})
user_detail = MyUserViewSet.as_view({
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy',
})
urlpatterns = [
path('login', obtain_jwt_token),
path('users/', user_list), #用于用户注册
path('users/<pk>/', user_detail), # 查找、更新、删除
]
####views中的配置
class MyUserViewSet(CustomModelViewSet):
queryset = MyUser.objects.all()
serializer_class = MyUserRegSerializer
authentication_classes = (JSONWebTokenAuthentication,)#指定需要哪一种权限验证方式
def get_serializer_class(self):
if self.action == "create":
return MyUserRegSerializer
elif self.action == "retrieve":
return MyUserUpdate
elif self.action == "update":
return MyUserUpdate
return MyUserUpdate
def get_permissions(self):
if self.action == "retrieve":
return [permissions.IsAuthenticated()]
elif self.action == "update":
return [permissions.IsAuthenticated()]
else:
# return []
return []
# 获取当前用户
def get_object(self):
return self.request.user
#######在serializer中
from .models import *
import re
from rest_framework import serializers
# 用户注册序列化类
class MyUserRegSerializer(serializers.ModelSerializer):
class Meta:
model = MyUser
fields = ("id", "username", "password", "mobile", "wx", "qq", "mibao")
def create(self, validated_data):
user = super().create(validated_data=validated_data)
user.set_password(validated_data["password"])
user.save()
return user
def validate_username(self, username):
# 判断用户名是否注册
if MyUser.objects.filter(username=username).count():
raise serializers.ValidationError("用户名已经存在,请查询")
# return JsonResponse({'errors': serializers.errors}, status=500)
return username
def validate_mobile(self, mobile):
# 判断手机号码是否注册
if MyUser.objects.filter(mobile=mobile).count():
raise serializers.ValidationError("手机号码已经存在,请查询")
# 手机号码正则表达式
REGEX_MOBILE = "^1[345789]\d{9}$|^147\d{8}$|^176\d{8}$"
# 验证手机号码是否合法
if not re.match(REGEX_MOBILE, mobile):
raise serializers.ValidationError("手机号码非法")
return mobile
# 用户资料更新
class MyUserUpdate(serializers.ModelSerializer):
class Meta:
model = MyUser
fields = ("username", "mobile", "wx", "qq", "mibao")
class get_id(serializers.ModelSerializer):
class Meta:
model = MyUser
fields = ("username", "wx", "qq")
#####在models中
from django.contrib.auth.models import AbstractUser
from django.db import models
class MyUser(AbstractUser):
username = models.CharField('姓名', blank=True, max_length=50, unique=True)
mobile = models.CharField('手机号码', max_length=11, default="", blank=True)
wx = models.CharField('微信号', max_length=20, default="", blank=True)
qq = models.CharField('qq号码', max_length=20, default="", blank=True)
mibao = models.CharField('密保问题', max_length=200, default="", blank=True)
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
def __str__(self):
return self.username
在试图函数里面设置权限
####在custommodelviewset里面定义权限
permission_classes = (IsAuthenticated,)
前端
vue+element_ui_plus的详细过程
文件配置
新建一个项目(vite),在webstormzhong完成的
安装element-ui-plus,并main.js文件里引入
npm install element-plus --save
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
安装router,并在main.js文件里引入
npm install vue-router --save
import router from "./router/index";
app.use(router)
安装vuex,并在main.js文件里引入
npm install vuex --save
import store from "@/vuex/store";
app.use(store)
安装axios,并做到所有请求统一管理
npm install axios --save
import axios from 'axios'
var URL = ''
// 创建 axios 实例
const service = axios.create({
baseURL: URL,
timeout: 6000 // 请求超时时间
})
// request拦截器
service.interceptors.request.use(config => {
}
)
// response拦截器
service.interceptors.response.use({}
)
export default service
目录介绍
src
src/api
专门统一化前端请求,在这里面模块画axios请求,例如
import request from '@/utils/request'
// 封装请求的方式
export function get_home_task(page) {
return request({
url: 'api/task/get/all?page='+page,
method: 'get',
})
}
//在组件里面这样用
import {get_home_task} from "@/api/task";
get_home_task().then((response) => {
this.mark_list = response.data.data;
});
src/assets
存放前端要用到的各种资源,包括图片,js,css,音频文件等
src/components
在里面存放组件,例如导航栏,侧栏,卡片之类的,就相当于html页面
src/router
有index.js文件,相当于django中的urls文件,用于路由分配,切换组件等
使用方法
import {createRouter, createWebHistory} from "vue-router";
const routes = [
#路由地址
{
path: "/us",
name: 'us',
component: us
}
]
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: routes
});
export default router;
src/utils
有requests,js,存放axios的请求拦截器,响应拦截器
使用用法
import axios from 'axios'
var URL = ''
// 创建 axios 实例
const service = axios.create({
baseURL: URL,
timeout: 6000 // 请求超时时间
})
// request拦截器
service.interceptors.request.use(config => {
const {url} = config
//指定页面访问需要JWT认证。
if (url.indexOf('api/task/img/picture/') !== -1 || url.indexOf('api/user/users/') !== -1 || url.indexOf('api/task/person') !== -1 || url === 'api/task/person/task/' || url === 'api/task/img/picture/') {
var jwt = localStorage.getItem('token');
config.headers.Authorization = 'JWT ' + jwt;
}
return config
},
error => {
Promise.reject(error)
}
)
// response拦截器
service.interceptors.response.use({}
)
export default service
src/vuex
里面有store.js文件,可以配置一些变量,方便在组件之间公用数据
import Vuex from 'vuex'
//定义便变量
const state = {
notice: "打开",
userinfo: localStorage["userinfo"],
token: localStorage["token"],
name: localStorage["name"],
id: localStorage["id"]
}
//定义同步方法
const mutations = {
saveUser(state, value) {
state.userinfo = value, localStorage.setItem('userinfo', value)
},
save_user_token(state, token) {
state.token = token, localStorage.setItem('token', token)
},
save_user_name(state, name) {
state.name = name, localStorage.setItem('name', name)
},
save_user_id(state, id) {
state.id = id, localStorage.setItem('id', id)
},
delUser(state) {
state.userinfo = '';
state.token = '';
state.name = '';
state.id = '';
localStorage['userinfo'] = '';
localStorage['token'] = '';
localStorage['name'] = '';
localStorage['token'] = '';
localStorage['id'] = '';
}
}
//用于取值
const getters = {
get_user_info(state) {
return state.userinfo
},
get_user_name(state){
return state.name
},
get_user_id(state){
return state.id
}
}
//注册前面几个东西
const store = new Vuex.Store({
state, mutations, getters
})
export default store
//在组件中的用法
import {mapGetters, mapMutations} from "vuex";
this.$store.status.xxxx
method:{
...mapMutations({
save_user_info: "saveUser"
})
}
computed: {
...mapGetters({
userinfo: 'get_user_info'
})
}
代理(解决前后端数据交互问题)
在vite.config.js文件里面
import {fileURLToPath, URL} from 'node:url'
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
// hmr: {overlay: false},
proxy: {
'/api': {
target: 'http://127.0.0.1:8000',//替换的服务端地址
changeOrigin: true,//开启代理,允许跨域
rewrite: path => path.replace(/^\/api/, "")//设置重写的路径
}
}
},
})
项目打包
npm run build