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... 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目录

查询数据库表

  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.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]解决跨域跨域请求失败

  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 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/

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值