> 文章列表 > Django入门

Django入门

Django入门

前言

django有一部分server功能(仅供调试,上线需要gunicorn,uwsgi等wsgi web server产品)
在这里插入图片描述

接口:可调用对象(如函数

流程

启动

  1. 创建django项目

  2. 创建app

    > python manage.py startapp [app名称]
    
  3. 注册app

    # setting.py INSTALLED_APPS 中追加
    "[app名称].apps.SalesConfig",
    
  4. 启动服务器

    > python manage.py runserver
    
  5. [app名称].view内写请求处理函数

     def listorders(request):return HttpResponse("下面是所有的订单信息")
    
  6. urls内添加url对应处理函数

     # urlpatterns 内追加path("sales/orders/", listorders),
    
    1. [app名称].urls.py(新建)内写子路由信息
      from django.urls import path
      from sales.views import listorders     urlpatterns = [path("orders/", listorders),
      ]
      
    2. 主路由表urls.py
      from django.urls import path,include  urlpatterns = [path("sales/", include('sales.urls')),# 相当于拼接url
      ]
      

数据库安装与连接

  1. 安装数据库 mysql 新建数据库
    mysql > create database [数据库名] DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
    
  2. 安装python包 mysqlclient
     > pip install mysqlclient
    
  3. django 连接数据库
    setting中修改DATABASES为
    DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME':'[数据库名]','USER': 'root','PASSWORD': '[数据库密码]', 'HOST': '127.0.0.1',#哪台机器安装了mysql'PORT': '3306',}
    }
    

创建更新数据库表

  1. [app名称].model中定义类以定义数据库表
    class Customer(models.Model):# 客户名称name =models.CharField(max_length=200)# 联系电话phonenumber = models.CharField(max_length=200)# 地址address = models.CharField(max_length=200)
    
  2. django将类定义转化为sql语句(生成[app名称].migration.0001_initial.py文件)
    > python manage.py makemigrations [app名称]Migrations for '[app名称]':common\\migrations\\0001_initial.py- Create model Customer
  3. 更新数据库(将django中修改数据表的操作迁移到数据库)
    > python manage.py migrate Operations to perform:Apply all migrations: admin, auth, common, contenttypes, sessions
    Running migrations:......Applying common.0001_initial... OKApplying 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目录

查询数据库表

  1. 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})
    
  2. mgr.urls.urlpatterns中添加路由
    path("customers", listcustomers),
    
  3. 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.parsetry {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]解决跨域跨域请求失败

  1. 安装django-cors-headers模块
    > pip3 install django-cors-headers
    
  2. 注册app[setting.py]
    INSTALLED_APPS = [...'corsheaders'
    ]
    
  3. 添加中间件[setting.py]
    MIDDLEWARE = [...'corsheaders.middleware.CorsMiddleware'
    ]
    
  4. 跨域设置[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",...
      ]
      

[mgr customer.py]

import json
from django.http import JsonResponse
from common.models import Customerdef 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.parsetry {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 = indexform.address = Data.value[index].addressform.name = Data.value[index].nameadd0.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 jsonfrom 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,pprintpayload = {'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_urlsurlpatterns = [...re_path(r"^docs/",include_docs_urls(title="My api title"))
]

runserver http://127.0.0.1:8000/docs/