前言
django有一部分server功能(仅供调试,上线需要gunicorn,uwsgi等wsgi web server产品)
接口:可调用对象(如函数)
流程
启动
-
创建django项目
-
创建app
> python manage.py startapp [app名称]
-
注册app
# setting.py INSTALLED_APPS 中追加 "[app名称].apps.SalesConfig",
-
启动服务器
> python manage.py runserver
-
[app名称].view内写请求处理函数
def listorders(request): return HttpResponse("下面是所有的订单信息")
-
urls内添加url对应处理函数
# urlpatterns 内追加 path("sales/orders/", listorders),
- 路由子表
- [app名称].urls.py(新建)内写子路由信息
from django.urls import path from sales.views import listorders urlpatterns = [ path("orders/", listorders), ]
- 主路由表urls.py
from django.urls import path,include urlpatterns = [ path("sales/", include('sales.urls')),# 相当于拼接url ]
数据库安装与连接
- 安装数据库 mysql 新建数据库
mysql > create database [数据库名] DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
- 安装python包 mysqlclient
> pip install mysqlclient
- django 连接数据库
setting中修改DATABASES为DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME':'[数据库名]', 'USER': 'root', 'PASSWORD': '[数据库密码]', 'HOST': '127.0.0.1',#哪台机器安装了mysql 'PORT': '3306', } }
创建更新数据库表
- [app名称].model中定义类以定义数据库表
class Customer(models.Model): # 客户名称 name =models.CharField(max_length=200) # 联系电话 phonenumber = models.CharField(max_length=200) # 地址 address = models.CharField(max_length=200)
- django将类定义转化为sql语句(生成[app名称].migration.0001_initial.py文件)
> python manage.py makemigrations [app名称] Migrations for '[app名称]': common\migrations\0001_initial.py - Create model Customer
- 更新数据库(将django中修改数据表的操作迁移到数据库)
> python manage.py migrate Operations to perform: Apply all migrations: admin, auth, common, contenttypes, sessions Running migrations: ...... Applying common.0001_initial... OK Applying sessions.0001_initial... OK
创建超级管理员 pwd 至少八位(8个8)
> python manage.py createsuperuser
Username (leave blank to use 'zimoxuan'): ZMX
Email address:
Password:
Password (again):
This password is too common.
This password is entirely numeric.
Bypass password validation and create user anyway? [y/N]: Y
Superuser created successfully.
auth_user 多一条记录(密码为加密过的)
通过 http://127.0.0.1:8000/admin/ 登录
- 如果想要在admin页面显示刚刚添加的common_Customer表,在common.model中添加
from django.contrib import admin admin.site.register(Customer)
新建app目录 mgr [startapp]
- mgr可能有很多视图函数,为了便于区分管理,可以不必都写到view里,在app目录下新建多个py文件以分别写不同逻辑下的视图函数
- 新建mgr.yrls和mgr.customer目录
查询数据库表
- mgr.customer撰写视图函数
from common.models import Customer def listcustomers(request): # 获取一个包含所有的表记录的 QuerySet 对象, qs = Customer.objects.values() # 将 QuerySet 对象转化为 list 类型 以便后续将其转化为 JSON 字符串 relist = list(qs) return JsonResponse({'ret':0,'relist':relist})
- mgr.urls.urlpatterns中添加路由
path("customers", listcustomers),
- urls.urlpatterns中添加路由
path("api/mgr/", include('mgr.urls')),
前后端连接
浏览器端代码[customers.vue]
<script setup>
import axios from 'axios'
import ref from 'vue'
let Data = ref([])
async function getCustomers() {//不需要JSON.parse
try {
const response = await axios.get('http://localhost:8000/api/mgr/customers');
Data.value = response.data['relist']
// console.log('third')
} catch (error) {
console.error(error);
}
}
getCustomers()
</script>
<template>
<!-- <span>Message: {{ Data }}</span> -->
<el-table :data="Data" max-height="250" style="width: 100%">
<el-table-column prop="id" label="id" width="180"/>
<el-table-column prop="name" label="Name" width="180"/>
<el-table-column prop="address" label="Address"/>
</el-table>
</template>
错误记录
- error 301:
url最后没有加’/’ - error CORS错误:
跨域跨域请求失败
服务器端[django]解决跨域跨域请求失败
- 安装django-cors-headers模块
> pip3 install django-cors-headers
- 注册app[setting.py]
INSTALLED_APPS = [ ... 'corsheaders' ]
- 添加中间件[setting.py]
MIDDLEWARE = [ ... 'corsheaders.middleware.CorsMiddleware' ]
- 跨域设置[setting.py]
- 允许所有来源访问
CORS_ORIGIN_ALLOW_ALL = True - 允许部分来源访问
CORS_ORIGIN_ALLOW_ALL = False
CORS_ORIGIN_WHITELIST = [‘http://example.com’ #允许访问的来源]- 注意:
来源必须标明:ip,端口,协议,而且ip,协议,端口一一对应才能获取
比如说你选了http://127.0.0.1:1000 你发起请求时http://localhost:1000 数据就没法获得
- 注意:
- 允许所有来源访问
dispatcher 路由分发函数 [postman]
- Django 的url路由功能 不支持直接 根据HTTP请求的方法和请求体里面的参数进行路由
- 解决方法:自己编写一个dispatcher函数来根据HTTP请求的类型和请求体里面的参数分发(或者说路由)给不同的函数进行处理
- POST,PUT请求时,Django会进行CSRF校验,若客户端未做相应处理,就会不通过CSRF校验,所以测试阶段我们先临时禁掉CSRF校验
- 方法:setting中
MIDDLEWARE=[ ... # "django.middleware.csrf.CsrfViewMiddleware", ... ]
- 方法:setting中
[mgr customer.py]
import json
from django.http import JsonResponse
from common.models import Customer
def listcustomers(request):
# 获取一个包含所有的表记录的 QuerySet 对象,
qs = Customer.objects.values()
# 将 QuerySet 对象转化为 list 类型 以便后续将其转化为 JSON 字符串
relist = list(qs)
return JsonResponse({'ret':0,'relist':relist})
def addcustomer(request):
# print(request)
info = request.params['data']
# 从请求消息中获取要添加客户的信息 然后插入到数据库中
record = Customer.objects.create(name = info['name'],phonenumber=info['phonenumber'],address=info['address'])
# record 代表一条记录
return JsonResponse({'ret':0,'id':record.id})
def modifycustomer(request):
# 从请求消息中获取修改客户的信息
# 找到该客户,并进行修改操作
customerid = request.params['id']
newdata = request.params['newdata']
try:
#根据ID从数据库中找到相应的客户记录
customer = Customer.objects.get(id = customerid)
except Customer.DoesNotExist:
return {
'ret':1,
'msg':f'id为`{customerid}`的客户不存在'
}
if 'name' in newdata:
customer.name = newdata['name']
if 'phonenumber' in newdata:
customer.phonenumber = newdata['phonenumber']
if 'address' in newdata:
customer .address = newdata['address']
# 注意,一定要执行save才能将修改信息保存到数据库
customer.save()
return JsonResponse({'ret': 0})
def deletecustomer(request):
customerid = request.params['id']
try:
# 根据 id 从数据库中找到相应的客户记录
customer = Customer.objects.get(id=customerid)
except Customer.DoesNotExist:
return{
'ret': 1,
'msg': f'id 为`{customerid}`的客户不存在'
}
# delete 方法就将该记录从数据库中删除了
customer.delete()
return JsonResponse({'ret': 0})
def dispatcher(request):
# 将请求参数统一放到request的params属性中,方便后续处理 WSGIRequest
# GET请求 参数从request对象的GET属性中获取
if request.method == 'GET' :
request.params = request.GET
# POST/PUT/DELETE请求 参数从request对象的body属性中获取
elif request.method in ['POST','PUT','DELETE']:
request.params = json.loads(request.body)
# 根据不同的action分派给不同的函数进行处理
action = request.params['action']
if action == 'list_customer':
return listcustomers(request)
elif action == 'add_customer':
return addcustomer(request)
elif action == 'modify_customer':
return modifycustomer(request)
elif action == 'delete_customer':
return deletecustomer(request)
else :
return JsonResponse({'ret':1,'msg':'不支持该类型http请求'})
前端完整页面[customers.vue]
[!!! API接口文档的重要性]
<script setup>
import axios from 'axios'
import {reactive, ref, shallowRef} from 'vue'
const Data = ref([])
let index0 = Data.value.length + 1
const form = reactive({
name: '',
address: '',
// phonenumer: ''
})
const add0 = shallowRef('add')
const cancel = () => {
form.name = ''
form.address = ''
add0.value = 'add'
index0 = Data.value.length + 1
}
async function getUser() {//不需要JSON.parse
try {
const response = await axios.get('http://localhost:8000/api/mgr/customers', {
params: {action: 'list_customer'}
});
Data.value = response.data['relist']
// console.log('third')
} catch (error) {
console.error(error);
}
}
getUser()
const onModifyItem = (index) => {
index0 = index
form.address = Data.value[index].address
form.name = Data.value[index].name
add0.value = 'edit'
}
const onSubmit = () => {
if (add0.value === 'edit') {
axios.put('http://localhost:8000/api/mgr/customers', {
action: 'modify_customer',
id: Data.value[index0].id,
newdata: {
name: form.name,
phonenumber: '8888',
address: form.address
}
})
.then(function (response) {
getUser()
// console.log(response);
})
.catch(function (error) {
console.log(error);
})
}
else {
axios.post('http://localhost:8000/api/mgr/customers', {
action: 'add_customer',
data: {
name: form.name,
phonenumber: 'Flintstone',
address: form.address
}
})
.then(function (response) {
getUser()
// console.log('second')
// console.log(response);
})
.catch(function (error) {
console.log(error);
})
}
cancel()
// console.log('first')
}
const onDeleteItem = (index) => {
axios.put('http://localhost:8000/api/mgr/customers', {
action: 'delete_customer',
id: Data.value[index].id,
})
.then(function (response) {
getUser()
// console.log(response);
})
.catch(function (error) {
console.log(error);
})
}
</script>
<template>
<div>
<h3>信息展示列表</h3>
<!-- <span>Message: {{ Data }}</span> -->
<el-table :data="Data" max-height="250" style="width: 100%">
<el-table-column prop="id" label="id" width="180"/>
<el-table-column prop="name" label="Name" width="180"/>
<el-table-column prop="address" label="Address"/>
<el-table-column fixed="right" label="Operations" width="120">
<template #default="scope">
<el-button link type="primary" size="small" @click.prevent="onModifyItem(scope.$index)">Edit</el-button>
<el-button link type="primary" size="small"
@click.prevent="onDeleteItem(scope.$index)">Delete
</el-button>
</template>
</el-table-column>
</el-table>
<br/>
<el-form :model="form" label-width="120px">
<el-form-item label="Name">
<el-input v-model="form.name"/>
</el-form-item>
<el-form-item label="Adress">
<el-input v-model="form.address"/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit()" style="width: 20%">
{{ add0 }}
</el-button>
<el-button @click="cancel" style="width: 20%">Cancel</el-button>
</el-form-item>
</el-form>
</div>
</template>
登录验证
mgr新建sign_in_out.py文件处理登录
import json
from django.contrib.auth import authenticate,login,logout
from django.http import JsonResponse
# 登录处理
def signin(request):
print(request.body)
# 从HTTP POST 请求中获取用户名、密码
userName = request.POST.get('username')
passWord = request.POST.get('password')
print(userName, passWord)
# 使用Django auth库里面的方法校验用户名密码
user = authenticate(username=userName,password=passWord)
# 找到用户且密码正确
if user is not None:
if user.is_active:
if user.is_superuser:
login(request,user)
# 在session 中存入用类型
request.session['usertype'] = 'mgr'
return JsonResponse({'ret':0})
else:
return JsonResponse({'ret':1,'msg':'请使用管理员账户登录'})
else:
return JsonResponse({'ret':1,'msg':'用户已被禁用'})
# 用户名密码错误
else:
return JsonResponse({'ret':1,'msg':'用户名或密码错误'})
# 登出处理
def signout(request):
logout(request)
return JsonResponse({'ret':0})
urls 配置路由
...
from mgr.sign_in_out import signin,signout
urlpatterns = [
...
path("signin", signin),
path("signout", signout),
]
新建tests目录 使用request模拟HTTP请求
import requests,pprint
payload = {
'username':'ZMX',
'password':'88888888'
}
response = requests.post('http://127.0.0.1:8000/api/mgr/signin',data=payload)
pprint.pprint(response.json())
前端登录页面请求
const sign = () => {
console.log(form.name,form.password)
axios.post('http://localhost:8000/api/mgr/signin', `username=${form.name}&password=${form.password}`)
.then(function (response) {
console.log(response.data.msg)
// console.log(response);
})
.catch(function (error) {
console.log(error);
})
}
session and token [未成功]
- django_session表
- sessionkey id
- sessiondata 存入信息
在用户登录成功后,服务端就在数据库session表中 中创建一条记录,记录这次会话。也就是创建一个新的 sessionid 插入到 该表中。同时也 放入一些 该session对应的数据到 记录的数据字段中,比如登录用户的 信息。然后在该登录请求的HTTP响应消息中,的头字段 Set-Cookie 里填入sessionid 数据。类似这样Set-Cookie:Tsessionid-6qu1cuk8cxvtf4w9rjxeppexh2izy@hh
根据http协议,这个Set-Cookie字段的意思就是 要求前端将其中的数据存入cookie中。并随后访问该服务端的时候,在HTTP请求消息中必须带上 这些 cookie数据cookie 通常就是存储在客户端浏览器的一些数据。 服务端可以通过http响应消息 要求 浏览器存储 一些数据以后每次访问 同一个网站服务,必须在HTTP请求中再带上 这些cookie里面的数据。cookie数据由多个 键值对组成,比如:
sessionid=6qu1cuk8cxvtf4w9rjxeppexh2izyOhhuser
name=byhy
favorite=phone laptop watch
该用户的后续操作,触发的HTTP请求,都会在请求头的Cookie字段带上前面说的sessionid 。
如下所示
Cookie: sessionid=6qu1cuk8cxvtf4w9rjxeppexh2izy0hh
服务端接受到该请求后,只需要到session表中查看是否有该 sessionid 对应的记录,这样就可以判断这个请求是否是前面已经登录的用户发出的。如果不是,就可以拒绝服务,重定向http请求到登录页面让用户登录
def dispatcher(request):
# 根据session判断用户是否是登陆过的管理员用户
if 'usertype' not in request.session:
return JsonResponse({
'ret':302,
'msg':'未登录',
'redirect':'http://127.0.0.1:5137'
},status=302)
if request.session['usertype']!='mgr':
return JsonResponse({
'ret': 302,
'msg': '非管理员用户',
'redirect': 'http://127.0.0.1:5137'
},status=302)
...
配置 swagger 接口文档 —[打不开 ]
- 安装drf-yasg2
> pip install drf-yasg2
- settings注册drf_yasg2
INSTALLED_APPS = [ ... 'drf_yasg2', ] SWAGGER_SETTINGS = { "DEFAULT_GENERATOR_CLASS": "rest_framework.schemas.generators.BaseSchemaGenerator", } # 报错之后新加的
- 配置url
from rest_framework import permissions from drf_yasg2.views import get_schema_view from drf_yasg2 import openapi schema_view = get_schema_view( openapi.Info( title="Tweet API", default_version='v1', description="Welcome to the world of Tweet", terms_of_service="https://www.tweet.org", contact=openapi.Contact(email="demo@tweet.org"), license=openapi.License(name="Awesome IP"), ), public=True, # permission_classes=(permissions.AllowAny,), ) urlpatterns = [ ... path("swagger/",schema_view.with_ui("swagger",cache_timeout=0),name="schema-swagger"), path("redoc/",schema_view.with_ui("redoc",cache_timeout = 0),name="schema-redoc"), ]
- runserver http://127.0.0.1:8000/swagger/ http://127.0.0.1:8000/redoc/
Django自带接口文档[好像是打开了没看懂]
[setting]
INSTALLED_APPS = [
...
"rest_framework",
]
[urls]
```python
...
from django.urls import re_path
from rest_framework.documentation import include_docs_urls
urlpatterns = [
...
re_path(r"^docs/",include_docs_urls(title="My api title"))
]
runserver http://127.0.0.1:8000/docs/