Django+Vue 五十個常用技巧


1 linux查看端口命令

netstat -ntlp   			# 查看當前所有tcp端口

netstat -ntulp |grep 80   	# 查看所有80端口使用情況

netstat -an | grep 3306   	# 查看所有3306端口使用情況

kill -9 3306				# 殺死使用3306端口的進程

2 ModelViewSet 路由

from django.urls import path
from rest_framework.routers import DefaultRouter

from user import views

router = DefaultRouter()
router.register(r'user', views.UserViewSet)

urlpatterns += router.urls

3 Vue模塊安裝錯誤

3.1 出現問題

94% asset optimization
ERROR  Failed to compile with 2 errors                                                                         19:47:42

These dependencies were not found:

* babel-runtime/core-js/json/stringify in ./src/http/index.js
* babel-runtime/core-js/promise in ./src/http/index.js

To install them, you can run: npm install --save babel-runtime/core-js/json/stringify babel-runtime/core-js/promise

3.2 解決方案

// 提示
// 分別安裝幾個包,就可以解決了!
>cnpm install --save babel-runtime
>cnpm install --save core-js
>cnpm install --save json
>cnpm install --save stringify

4 Serializer修改數據

4.1 models.py

class Books(BaseModel):
    name = models.CharField(max_length=30)
    publish = models.CharField(max_length=30)
    read_num = models.IntegerField()  # 閱讀量
    comment_num = models.IntegerField()  # 評論量

    class Meta:
        db_table = 'tb_books'

    def __str__(self):
        return self.name

4.2 serializer.py

class BooksModelSer(serializers.ModelSerializer):
    class Meta:
        model = Books
        fields = '__all__'

4.3 views.py

class BooksView(APIView):
    def put(self,request):
        print(request.data)
        id = request.data.get('id')
        books_obj = Books.objects.get(id=id)
        books_ser = BooksModelSer(data=request.data,instance=books_obj)
        books_ser.is_valid()
        books_ser.save()
        return Response({'msg': 'ok', 'code': 200})

4.4 urls.py

path('books/',BooksView.as_view())

5 element-ui 安裝命令

cnpm i element-ui -S

6 json獲取request的數據

body_json = request.body.decode()
body_dict = json.loads(body_json)

7 跨域后端

INSTALLED_APPS = [
	'corsheaders'
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware'
]

CORS_ORIGIN_WHITELIST = (
    'http://127.0.0.1:8080',
    'http://localhost:8080',
)
CORS_ALLOW_CREDENTIALS = True

8 跨域前端

8.1 http.js

import axios from 'axios'

// 第一步:設置axios
axios.defaults.baseURL = "http://192.168.56.100:1594/"

//全局設置網絡超時
axios.defaults.timeout = 10000;

//設置請求頭信息
axios.defaults.headers.post['Content-Type'] = 'application/json';
axios.defaults.headers.put['Content-Type'] = 'application/json';


// 第二:設置攔截器
/**
 * 請求攔截器(當前端發送請求給后端前進行攔截)
 * 例1:請求攔截器獲取token設置到axios請求頭中,所有請求接口都具有這個功能
 * 例2:到用戶訪問某一個頁面,但是用戶沒有登錄,前端頁面自動跳轉 /login/ 頁面
 */
axios.interceptors.request.use(
    config => {
        // 每次發送請求之前判斷是否存在token,如果存在,則統一在http請求的header都加上token,不用每次請求都手動添加了
        const token = localStorage.getItem("token")
            // console.log(token)
        if (token) {
            config.headers.Authorization = 'JWT ' + token
        }
        return config;
    },
    error => {
        return Promise.error(error);
    })

axios.interceptors.response.use(
    // 請求成功
    res => res.status === 200 ? Promise.resolve(res) : Promise.reject(res),
    // 請求失敗
    error => {
        if (error.response) {
            // 判斷一下返回結果的status == 401?  ==401跳轉登錄頁面。  !=401passs
            // console.log(error.response)
            if (error.response.status === 401) {
                // 跳轉不可以使用this.$router.push方法、
                // this.$router.push({path:'/login'})
                window.location.href = "http://127.0.0.1:8888/"
            } else {
                // errorHandle(response.status, response.data.message);
                return Promise.reject(error.response);
            }
            // 請求已發出,但是不在2xx的范圍
        } else {
            // 處理斷網的情況
            // eg:請求超時或斷網時,更新state的network狀態
            // network狀態在app.vue中控制着一個全局的斷網提示組件的顯示隱藏
            // 關於斷網組件中的刷新重新獲取數據,會在斷網組件中說明
            // store.commit('changeNetwork', false);
            return Promise.reject(error.response);
        }
    });


// 第三:封裝axios請求
// 3.1 封裝get請求
export function axios_get(url, params) {
    return new Promise(
        (resolve, reject) => {
            axios.get(url, {params:params})
                .then(res => {
                    // console.log("封裝信息的的res", res)
                    resolve(res.data)
                }).catch(err => {
                    reject(err.data)
                })
        }
    )
}

// 3.2 封裝post請求
export function axios_post(url, data) {
    return new Promise(
        (resolve, reject) => {
            // console.log(data)
            axios.post(url, JSON.stringify(data))
                .then(res => {
                    // console.log("封裝信息的的res", res)
                    resolve(res.data)
                }).catch(err => {
                    reject(err.data)
                })
        }
    )
}

// 3.3 封裝put請求
export function axios_put(url, data) {
    return new Promise(
        (resolve, reject) => {
            // console.log(data)
            axios.put(url, JSON.stringify(data))
                .then(res => {
                    // console.log("封裝信息的的res", res)
                    resolve(res.data)
                }).catch(err => {
                    reject(err.data)
                })
        }
    )
}

// 3.4 封裝delete請求
export function axios_delete(url, data) {
    return new Promise(
        (resolve, reject) => {
            // console.log(data)
            axios.delete(url, { params: data })
                .then(res => {
                    // console.log("封裝信息的的res", res)
                    resolve(res.data)
                }).catch(err => {
                    reject(err.data)
                })
        }
    )
}

8.2 apis.js

//將我們http.js中封裝好的  get,post.put,delete  導過來
import { axios_get, axios_post, axios_delete, axios_put } from './index.js'


// 書籍管理接口
export const getBookList = (params, headers) => axios_get("/books/book/", params, headers)
export const addBook = (params, headers) => axios_post("/books/book/", params, headers)
export const updateBook = (params, headers) => axios_put("/books/book/", params, headers)
export const delBook = (params, headers) => axios_delete("/books/book/", params, headers)


//按照格式確定方法名
export const user_login = p => axios_post("/user/login/", p)  //
export const get_dept_list = p => axios_get("/account/deptManage/", p)  //

9 繼承AbstractUser

AUTH_USER_MODEL = 'user.User'

from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    username = models.CharField(max_length=30, unique=True)

10 settings 配置

"""
Django settings for opwf project.

Generated by 'django-admin startproject' using Django 2.0.13.

For more information on this file, see
https://docs.djangoproject.com/en/2.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.0/ref/settings/
"""
import datetime
import os, sys

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'uorj1ni^mnut@wo@c%)iv)%5=8dxlml4-j0!f3b%4#f*8a5)3t'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['*']


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'corsheaders',
    'user.apps.UserConfig',
    'workflow.apps.WorkflowConfig',
    'workerorder.apps.WorkerorderConfig',
    # 'jwt',
    # 'rest_framework_jwt',
    # 'rest_framework.authentication'

]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'corsheaders.middleware.CorsMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'opwf.urls'
CORS_ORIGIN_ALLOW_ALL = True

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'opwf.wsgi.application'


# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'opwf_db',
        'USER': 'root',
        'PASSWORD': '1',
        'HOST': '127.0.0.1',
        'PORT': '3306'
    }
}


# Password validation
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators

REST_FRAMEWORK = {
    # 文檔報錯: AttributeError: ‘AutoSchema’ object has no attribute ‘get_link’
    # 用下面的設置可以解決
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
    # 默認設置是:
    # 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.openapi.AutoSchema',

    # 異常處理器
    # 'EXCEPTION_HANDLER': 'user.utils.exception_handler',

    # Base API policies      默認渲染器類
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ],
    # 默認解析器類
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser'
    ],
    # 1.認證器(全局)
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',  # 在 DRF中配置JWT認證
        # 'rest_framework.authentication.SessionAuthentication',  # 使用session時的認證器
        # 'rest_framework.authentication.BasicAuthentication'  # 提交表單時的認證器
    ],

    # 2.權限配置(全局): 順序靠上的嚴格
    'DEFAULT_PERMISSION_CLASSES': [
        # 'rest_framework.permissions.IsAdminUser',  # 管理員可以訪問
        # 'rest_framework.permissions.IsAuthenticated',  # 認證用戶可以訪問
        # 'rest_framework.permissions.IsAuthenticatedOrReadOnly',  # 認證用戶可以訪問, 否則只能讀取
        'rest_framework.permissions.AllowAny',  # 所有用戶都可以訪問
    ],
    # 3.限流(防爬蟲)
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle',
    ],
    # 3.1限流策略
    # 'DEFAULT_THROTTLE_RATES': {
    #     'user': '100/hour',  # 認證用戶每小時100次
    #     'anon': '300/day',  # 未認證用戶每天能訪問3次
    # },

    'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',
    'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
    'DEFAULT_VERSIONING_CLASS': None,

    # 4.分頁(全局):全局分頁器, 例如 省市區的數據自定義分頁器, 不需要分頁
    # 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    # 每頁返回數量
    # 'PAGE_SIZE': 1
    # 5.過濾器后端
    'DEFAULT_FILTER_BACKENDS': [
        'django_filters.rest_framework.DjangoFilterBackend',
        # 'django_filters.rest_framework.backends.DjangoFilterBackend', 包路徑有變化
    ],

    # 5.1過濾排序(全局):Filtering 過濾排序
    'SEARCH_PARAM': 'search',
    'ORDERING_PARAM': 'ordering',

    'NUM_PROXIES': None,

    # 6.版本控制:Versioning  接口版本控制
    'DEFAULT_VERSION': None,
    'ALLOWED_VERSIONS': None,
    'VERSION_PARAM': 'version',

    # Authentication  認證
    # 未認證用戶使用的用戶類型
    'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
    # 未認證用戶使用的Token值
    'UNAUTHENTICATED_TOKEN': None,

    # View configuration
    'VIEW_NAME_FUNCTION': 'rest_framework.views.get_view_name',
    'VIEW_DESCRIPTION_FUNCTION': 'rest_framework.views.get_view_description',

    'NON_FIELD_ERRORS_KEY': 'non_field_errors',

    # Testing
    'TEST_REQUEST_RENDERER_CLASSES': [
        'rest_framework.renderers.MultiPartRenderer',
        'rest_framework.renderers.JSONRenderer'
    ],
    'TEST_REQUEST_DEFAULT_FORMAT': 'multipart',

    # Hyperlink settings
    'URL_FORMAT_OVERRIDE': 'format',
    'FORMAT_SUFFIX_KWARG': 'format',
    'URL_FIELD_NAME': 'url',

    # Encoding
    'UNICODE_JSON': True,
    'COMPACT_JSON': True,
    'STRICT_JSON': True,
    'COERCE_DECIMAL_TO_STRING': True,
    'UPLOADED_FILES_USE_URL': True,

    # Browseable API
    'HTML_SELECT_CUTOFF': 1000,
    'HTML_SELECT_CUTOFF_TEXT': "More than {count} items...",

    # Schemas
    'SCHEMA_COERCE_PATH_PK': True,
    'SCHEMA_COERCE_METHOD_NAMES': {
        'retrieve': 'read',
        'destroy': 'delete'
    },

    # 'Access-Control-Allow-Origin':'http://localhost:8080',
    # 'Access-Control-Allow-Credentials': True

}

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/

LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/

STATIC_URL = '/static/'
AUTH_USER_MODEL = 'user.User'

# jwt載荷中的有效期設置
JWT_AUTH = {
    # 1.token前綴:headers中 Authorization 值的前綴
    'JWT_AUTH_HEADER_PREFIX': 'JWT',
    # 2.token有效期:一天有效
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
    # 3.刷新token:允許使用舊的token換新token
    'JWT_ALLOW_REFRESH': True,
    # 4.token有效期:token在24小時內過期, 可續期token
    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(hours=24),
    # 5.自定義JWT載荷信息:自定義返回格式,需要手工創建
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'user.utils.jwt_response_payload_handler',
}

11 幾個容易混淆的導包

# ModelViewSet
from rest_framework import viewsets

# APIView
from rest_framework.views import APIView

# Serializer
from rest_framework import serializers

# DefaultRouter
from rest_framework.routers import DefaultRouter

# AbstractUser
from django.contrib.auth.models import AbstractUser

12 ModelViewSet 路由三部曲

from django.urls import path
from rest_framework.routers import DefaultRouter

from user import views

router = DefaultRouter()
router.register(r'user', views.UserViewSet)

urlpatterns += router.urls

13 路由轉換器

  • 我們在定義一個標准路由的時候,可以給路由定義一些可變的參數,這些參數會傳遞給我們的視圖函數
from userapp.views import findgoods,findlove

urlpatterns = [
    path('goods/<int:id>/', findgoods),
    path('goods/<str:name>/', findlove)
]

注意:
路由轉換器:
我們可以在參數部分寫一個轉換器,參數值在傳遞到視圖函數之前,對參數進行格式轉換,默認我們的參數值都是字符串str格式,如果我們想把參數值轉成整型,我們可以使用int



from django.shortcuts import render, HttpResponse

# Create your views here.

goods_list=['蘋果手機','聯想電腦','大西瓜']

def findgoods(request, id):
    print(id, type(id))
    name = goods_list[id]
    return  HttpResponse("查找商品:"+name)

14 狀態碼大全

* http狀態返回代碼 1xx(臨時響應)
    表示臨時響應並需要請求者繼續執行操作的狀態代碼。
代碼說明:
100   (繼續)請求者應當繼續提出請求。服務器返回此代碼表示已收到請求的第一部分,正在等待其余部分。
101   (切換協議)請求者已要求服務器切換協議,服務器已確認並准備切換。


** http狀態返回代碼
    2xx (成功)表示成功處理了請求的狀態代碼。
代碼說明:
200   (成功)  服務器已成功處理了請求。通常,這表示服務器提供了請求的網頁。
201   (已創建)  請求成功並且服務器創建了新的資源。
202   (已接受)  服務器已接受請求,但尚未處理。
203   (非授權信息)  服務器已成功處理了請求,但返回的信息可能來自另一來源。
204   (無內容)  服務器成功處理了請求,但沒有返回任何內容。
205   (重置內容)服務器成功處理了請求,但沒有返回任何內容。
206   (部分內容)  服務器成功處理了部分 GET 請求。


*** http狀態返回代碼 3xx (重定向)
    表示要完成請求,需要進一步操作。通常,這些狀態代碼用來重定向。
代碼說明:
300   (多種選擇)  針對請求,服務器可執行多種操作。服務器可根據請求者 (user agent) 選擇一項操作,或提供操作列表供請求者選擇。
301   (永久移動)  請求的網頁已永久移動到新位置。服務器返回此響應(對 GET 或 HEAD 請求的響應)時,會自動將請求者轉到新位置。
302   (臨時移動)  服務器目前從不同位置的網頁響應請求,但請求者應繼續使用原有位置來進行以后的請求。
303   (查看其他位置)請求者應當對不同的位置使用單獨的 GET 請求來檢索響應時,服務器返回此代碼。


304   (未修改)自從上次請求后,請求的網頁未修改過。服務器返回此響應時,不會返回網頁內容。
305   (使用代理)請求者只能使用代理訪問請求的網頁。如果服務器返回此響應,還表示請求者應使用代理。
307   (臨時重定向)  服務器目前從不同位置的網頁響應請求,但請求者應繼續使用原有位置來進行以后的請求。


**** http狀態返回代碼 4xx(請求錯誤)
    這些狀態代碼表示請求可能出錯,妨礙了服務器的處理。
代碼說明:
400   (錯誤請求)服務器不理解請求的語法。
401   (未授權)請求要求身份驗證。對於需要登錄的網頁,服務器可能返回此響應。
403   (禁止)服務器拒絕請求。
404   (未找到)服務器找不到請求的網頁。
405   (方法禁用)禁用請求中指定的方法。
406   (不接受)無法使用請求的內容特性響應請求的網頁。
407   (需要代理授權)此狀態代碼與 401(未授權)類似,但指定請求者應當授權使用代理。
408   (請求超時)  服務器等候請求時發生超時。
409   (沖突)  服務器在完成請求時發生沖突。服務器必須在響應中包含有關沖突的信息。
410   (已刪除)  如果請求的資源已永久刪除,服務器就會返回此響應。
411   (需要有效長度)服務器不接受不含有效內容長度標頭字段的請求。
412   (未滿足前提條件)服務器未滿足請求者在請求中設置的其中一個前提條件。
413   (請求實體過大)服務器無法處理請求,因為請求實體過大,超出服務器的處理能力。
414   (請求的 URI 過長)請求的 URI(通常為網址)過長,服務器無法處理。
415   (不支持的媒體類型)請求的格式不受請求頁面的支持。
416   (請求范圍不符合要求)如果頁面無法提供請求的范圍,則服務器會返回此狀態代碼。
417   (未滿足期望值)服務器未滿足"期望"請求標頭字段的要求。


***** http狀態返回代碼 5xx(服務器錯誤)
    這些狀態代碼表示服務器在嘗試處理請求時發生內部錯誤。這些錯誤可能是服務器本身的錯誤,而不是請求出錯。
代碼說明:
500   (服務器內部錯誤)  服務器遇到錯誤,無法完成請求。
從上往下讀,最上面都是拋出異常代碼,從上往下,慢慢找出錯誤產生的原生地方,一般都是最下面是錯誤出現的地方

501   (尚未實施)服務器不具備完成請求的功能。例如,服務器無法識別請求方法時可能會返回此代碼。
502   (錯誤網關)服務器作為網關或代理,從上游服務器收到無效響應。
503   (服務不可用)服務器目前無法使用(由於超載或停機維護)。通常,這只是暫時狀態。
504   (網關超時)  服務器作為網關或代理,但是沒有及時從上游服務器收到請求。
505   (HTTP 版本不受支持)服務器不支持請求中所用的 HTTP 協議版本。


一些常見的http狀態返回代碼為:
200 - 服務器成功返回網頁
404 - 請求的網頁不存在
503 - 服務不可用

15 請求報文

image-20201123075848880

16 響應報文

image-20201123080742883

17 json數據結構與使用方法

17.1 json的數據結構

json全稱: "JavaScript Object Notation",(JavaScript對象表示法),一種基於文本,獨立於語言的輕量級數據交換格式。

17.2 JSON規定的格式

​ 1)數據在鍵值對中

​ 2)數據由逗號分隔

​ 3)花括號保存對象

​ 4)方括號保存數組

  • 在我們python中,就認為成字典+列表組成就是我們的JSON

17.3 json數據的頁面響應

  • JsonResponse響應json數據

要想返回json數據,就要使用專門的Response對象JSONResponse

from django.shortcuts import render,HttpResponse

方法1

from django.http.response import JsonResponse

def student(requset):
    stu = {'name': "maple", 'age': 12, '愛好': '學習'}
    return JsonResponse(stu,json_dumps_params={'ensure_ascii':False})
    # 只能添加字典

------------------------------------------------------------------------------------------------------

方法2

import json

def student(request):
    stu = {'name': "maple", 'age': 12, '愛好': '學習'}
    s = json.dumps(stu, ensure_ascii=False)
    return HttpResponse(s)
  • json是一個模塊,可以把字典對象變成json字符串
import json
# 我們進行網絡傳輸,必須是字符串或者是二進制數據
# 使用python內置的json模塊來完成

stu = {'name': "maple", 'age': 12, '愛好': '學習'}
print(type(stu))
x = json.dumps(stu)
print(x)
# \u 是unicode碼值
y = json.dumps(stu,ensure_ascii=False)
print(y)

c = {'name': 'Marry', 'age': 12}

e = json.dumps(c)
print(e, type(e))		# {"name": "Marry", "age": 12} <class 'str'>
f = json.loads(e)
print(f, type(f))		# {'name': 'Marry', 'age': 12} <class 'dict'>

17.4 前端

  • 將json字符串轉換為json對象的方法。在數據傳輸過程中,json是以文本,即字符串的形式傳遞的,而JS操作的是JSON對象,所以,JSON對象和JSON字符串之間的相互轉換是關鍵
JSON字符串:
var str1 = '{ "name": "cxh", "sex": "man" }'; 
JSON對象:
var str2 = { "name": "cxh", "sex": "man" };

A. JSON字符串轉換為JSON對象

要使用上面的str1,必須使用下面的方法先轉化為JSON對象:

//由JSON字符串轉換為JSON對象

var obj = eval('(' + str + ')');

或者

var obj = str.parseJSON(); //由JSON字符串轉換為JSON對象

或者

var obj = JSON.parse(str); //由JSON字符串轉換為JSON對象

然后,就可以這樣讀取:

Alert(obj.name);

Alert(obj.sex);

特別注意:如果obj本來就是一個JSON對象,那么使用eval()函數轉換后(哪怕是多次轉換)還是JSON對象,但是使用parseJSON()函數處理后會有問題(拋出語法異常)。

B. 可以使用toJSONString()或者全局方法JSON.stringify()將JSON對象轉化為JSON字符串。
例如:

var last=obj.toJSONString(); //將JSON對象轉化為JSON字符

或者

var last=JSON.stringify(obj); //將JSON對象轉化為JSON字符
alert(last)

18 serializer重寫

class UserSerializer(serializers.Serializer):
    # 用serializer重寫,不能用ModelViewSet來添加,寫上id會報錯username不能設為key,因為User表繼承的是AbstractUser,其中的username字段必須有unique=True屬性設置唯一約束
    username = serializers.CharField()
    password = serializers.CharField()
    mobile = serializers.CharField()
    email = serializers.EmailField()
    token = serializers.CharField(read_only=True)

    def create(self, data):
        username = data.get('username', '')
        password = data.get('password', '')
        mobile = data.get('mobile', '')
        email = data.get('email', '')
        user = User(username=username, email=email, mobile=mobile)
        user.set_password(password)
        user.save()
        # 補充生成記錄登錄狀態的token
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        user.token = token
        return user

	def update(self, instance, validated_data):
        # instance 是當前對象obj
        # validated_data 是獲取的變量
		instance.mobile = validated_data.get('mobile')
		instance.email = validated_data.get('email')
		instance.save()
		return instance

19 要注意ModelViewSet的接口路由

  • 后端接口
* 查詢接口
http://192.168.56.100:1594/user/user/
http://192.168.56.100:1594/user/user/?username='cat'
* 添加接口
http://192.168.56.100:1594/user/user/
* 刪除接口(指定用戶id刪除)
http://192.168.56.100:1594/user/user/id/
* 修改接口(指定用戶id修改)
http://192.168.56.100:1594/user/user/id/
  • 前端訪問路徑配置
export const get_userlist = P => axios_get('/user/user/', P)     
// 查詢所有:獲取用戶列表
export const search_for = P => axios_get('user/user/?username=' + P.username)   
// 查找指定信息:根據用戶名查找指定用戶信息並展示
export const add_user = P => axios_post('/user/user/', P)     
// 添加用戶信息
export const delete_user = P => axios_delete('/user/user/' + P + '/')           
// 刪除指定信息:根據獲取到的用戶id刪除用戶信息
export const update_user = P => axios_put('/user/user/'+ P.id +'/', P)    
// 修改指定信息:根絕用戶id和提交來的數據修改用戶信息

20 使用 ant-design-vue

// 使用ant-design-vue
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';
Vue.config.productionTip = false
Vue.use(Antd);

21 ModelViewSet修改問題

  • put請求默認是全部修改
  • patch請求可以實現局部修改(后端已經封裝好了方法)

22 Vue嵌套路由

  • index.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/layout/Home'
// 用於導包路徑的配置
const page = name => () => import('@/views/' + name)
Vue.use(Router)

export default new Router({
  mode: 'history',
  routes: [
    // 用戶管理模塊
    { path: '/',component: Home,name: 'home',
      children: [
        { path: 'usermanage', component: page('user-manage/Index'), name: '用戶管理' },
        { path: 'flowconf', component: page('workflow/WorkFlowConf'), name: '模板管理' },
        { path: 'flowtype', component: page('workflow/WorkFlowType'), name: '模板類型管理' },
        { path: 'baidu', component: page('BaiDu'), name: '跳轉百度' },
      
      ]
    },
  ]
})
  • Home頁面中寫入
<template>
    <div id="components-layout-demo-basic">
         <a-layout>
              </a-layout-header>
                  <div>
                     <!-- 這里的 router-view 是綁定的路由 -->
                     <router-view></router-view>
                   </div>
              </a-layout-header>
          </a-layout>
    </div>
</template>

<script>
// 導入組件
import LeftMenu from '@/components/layout/LeftMenu'
import Header from '@/components/layout/Header'
export default {
    // 注冊組件
    components:{
        LeftMenu,
        Header
    },
}
  • 綁定左側菜單LeftMenu
<template>
  <div>
    <a-menu
      style="width: 256px"
      :default-selected-keys="['1']"
      :default-open-keys="['sub1']"
      :mode="mode"
      :theme="theme"
      @click="handleClick"
    >
    <!-- 必須定義click方法,handleClick是用來跳轉路由的,內置毀掉參數e,包括傳遞上去的key路由地址 -->    
      <a-menu-item key="usermanage">
        <!-- key是路由地址 -->
        用戶管理模塊
      </a-menu-item>

      <a-menu-item key="baidu">
        百度翻譯
      </a-menu-item>
    </a-menu>
  </div>
</template>
<script>
export default {
  data() {
    return {
    };
  },
  methods: {
     handleClick(e) {
      this.current = e.key;
      this.$router.push({path:this.current});
      // click方法默認回調參數中的 key,所以 e.key就是傳遞來的路由
    },
  },
};
</script>

23 ant-design-vue 圖標大小

  • font-size 可以調節圖標大小

24 v-if v-else控制input框disabled屬性

<template>
    <div>
        <a-modal
        title="Please write now."
        :visible="visible"
        @ok="handleOk"
        @cancel="handleCancel"
        >
        <!-- @ok控制按鈕ok -->
        <!-- @cancel控制按鈕cancel -->
            <p v-if="userList.id">UserName:
                <a-input 
                style="width:380px;float:right" 
                placeholder="username" 
                v-model="userList.username"
                disabled="disabled"
                ></a-input>    
                <!-- disabled可以用來控制input是否能輸入文字 -->
            </p>
            <p v-else>UserName:
                <a-input 
                style="width:380px;float:right" 
                placeholder="username" 
                v-model="userList.username"
                ></a-input>
            </p>
            <br>
            
            <div v-if="userList.id">

            </div>
            
            <div v-else>
            <p>PassWord:
                <a-input
                    v-decorator="[
                        'password',
                        { rules: [{ required: true, message: 'Please input your Password!' }] },
                    ]"
                    type="password"
                    placeholder="Password"
                    style="width:380px;float:right"
                    v-model="userList.password"
                    :disabled = 'false'
                >
                </a-input>
            </p>
        </a-modal>
    </div>
</template>

<script>
export default {
    props:['visible', 'userList'],
    methods: {
        handleOk(e) {
            this.$emit('add')
            // add方法的調用一定要在關閉彈窗上面,否則方法不執行完畢沒有辦法關閉彈窗        
            this.$emit('update:visible', false)
        },
        handleCancel(e) {
            this.$emit('update:visible', false)
        },
    },
}
</script>

25 a-table中的分頁器&按鈕

 <a-table 
    :columns="要展示的列名稱(可以自定義列表)" 
    :data-source="要展示的動態數據表名" 
    :pagination="pagination" 
    :rowKey="(record,index)=>{return index}"
  >
  <!-- 帶:的都是屬性綁定,不可以更換名字,帶 : 就是 js 環境 -->
  <!-- 不寫:rowKey="(record,index)=>{return index}"瀏覽器會發出警告 -->
     <p slot="tags" slot-scope="text,tags,i">
      <!-- 加入操作的按鈕!其中tags是某列數據,i是索引 -->
      <a-button @click="delUser(text,tags,i)">刪除</a-button>
    </p>
  </a-table>

26 a-table中獲取某一行值的方法

<template>
  <a-table 
    :columns="columns" 
    :data-source="userListGet" 
    :pagination="pagination" 
    :rowKey="(record,index)=>{return index}"
  >
  <!-- 帶:的都是屬性綁定,不可以更換名字,帶 : 就是 js 環境 -->
    <!-- 不寫:rowKey="(record,index)=>{return index}"瀏覽器會發出警告 -->
    <p slot="tags" slot-scope="text,tags,i">
      <!-- 加入操作的按鈕! -->
      <a-button @click="delUser(text,tags,i)">刪除</a-button>
    </p>
  </a-table>
</template>

<script>
const columns = [
  {
    title:'操作',
    dataIndex: 'tags',
    key : 'tags',
    width: 100,
    scopedSlots : { customRender: 'tags'}
    // scopedSlots: { customRender: 'tags' },一定不能少不然渲染不了html標簽
  }
]

import { delete_user } from '@/http/apis'
export default {
  data() {
    return {
      columns,//這里要返回值哦 
      pagination:{
      pageSize: 4,
      }
    };
  },
  methods:{
    delUser(text,tags,i){
       }     
    },
  }
</script>

27 控制對話框

// 定義變量 isDel來控制 confirm,isDel==true執行的就是對話框的 ok,isDel==false執行的就是對話框的 false
const isDel = confirm('你確定要刪除' + tags.id)
if(isDel==true){
	delete_user(tags.id).then(
		res=>{
		// 刪除回調地址是  http://192.168.56.100:1594/id/
		this.get()
			alert('刪除成功啦~')
		})
}else{
	alert('有需要再叫我哈~') 
}

28 自動跳轉到某一網址

window.location.href = 'https://www.baidu.com/'

29 Vue報錯無法查看對象問題

  • 出現問題
Invalid prop: type check failed for prop "dataSource". Expected Array, got Object 
  • 解決方案
this.userListGet = res.results

31 Vue+Django-RestFrameWork 實現分頁

  • 后端代碼

    • 實現局部分頁
    from django.shortcuts import render
    from rest_framework import viewsets
    from rest_framework.pagination import PageNumberPagination
    from rest_framework.response import Response
    from rest_framework.views import APIView
    
    from user.models import User
    from user.serializers import UserSerializer, UserModelSerializer
    
    # 分頁(局部):自定義分頁器 局部
    class PageNum(PageNumberPagination):
        page_size = 4                           # 每頁顯示多少條
        page_size_query_param = 'page_size'     # 查詢字符串中代表每頁返回數據數量的參數名, 默認值: None
        page_query_param = 'page'               # 查詢字符串中代表頁碼的參數名, 有默認值: page
        max_page_size = None                    # 最大頁碼數限制
        
    
    class UserViewSet(viewsets.ModelViewSet):
        queryset = User.objects.all()
        serializer_class = UserModelSerializer
        filter_fields = {"username"}
        pagination_class = PageNum  # 注意不是列表(只能有一個分頁模式)
    
  • 前端代碼

    • Pagination.vue
    <template>
      <div>
        <a-pagination
          show-quick-jumper
          :default-current="2"
          :pageSize = '4'
          :total="count"
          show-less-items
          @change="onChange"
          v-model="current"
        />
          <!-- 一定是change方法,不然不能跳轉 -->
      </div>
    </template>
    
    <script>
    export default {
        props:[ 'count' ],
        data() {
            return {
                current:1
            }
        },
        methods: { 
            onChange() {
                this.$emit('getPage', this.current)
        },
        },
        created() {
    
        }
    }
    </script>
    
    • Index.vue
    <template>
    <div>
      <div id="components-layout-demo-basic">
            <a-layout-content>
                <TableList
                      :userListGet="userListGet"
                      :userList="userList"
                      @getUser="getUser"
                      @add="add"
                 >
                        
                 </TableList>
                 <Pagination
                      @getPage="getPage" 
                      :count="count"              
                  >
        		  </Pagination>
            </a-layout-content>
      </div>
    </div>
    </template>
    
    <script>
    import Pagination from "./components/Pagination"
    
    import { get_userlist } from '@/http/apis';
    export default {
        components:{
            BreadCrumb,
            TableList,
            Search,
            EditForm,
            Pagination
        },
        data() {
            return {
                userListGet:[],
                updateUserList:[],
                // 當前頁碼
                current:1,
                // 總共的數據多少條
                count:0
    
            }
        },
        methods: {
            getUser(){
              // 獲取用戶信息列表,父組件傳遞給子組件
              get_userlist(this.current).then(res=>{
                this.userListGet = res.results
                this.count = res.count
                console.log(this.count)
                console.log(this.userListGet)
              })
            },
            // 獲取頁碼
            getPage(currentChild){
              // 獲取到的currentChild是子組件傳遞過來是第幾頁
              this.current = currentChild
              console.log(this.current)
              this.getUser()
            }
        },
        created() {
    
        }
    }
    </script>
    
    <style scoped>
    #components-layout-demo-basic {
      text-align: center;
    }
    #components-layout-demo-basic .ant-layout-header,
    #components-layout-demo-basic .ant-layout-footer {
      background: white;
      color: #fff;
    }
    #components-layout-demo-basic .ant-layout-footer {
      line-height: 1.5;
    }
    #components-layout-demo-basic .ant-layout-content {
      background: white;
      color: #fff;
      min-height: 120px;
      line-height: 120px;
    }
    #components-layout-demo-basic > .ant-layout {
      margin-bottom: 48px;
    }
    #components-layout-demo-basic > .ant-layout:last-child {
      margin: 0;
    }
    </style>
    
    

32 前后端聯調分頁

  • 為了解決前端+后端能同時進行分頁的問題,只定義前端分頁,后端如果分頁就會影響前端數據,如果后端不定義分頁器,就會造成后端admin難以管理。

  • 后端代碼

    • 實現局部分頁
    from django.shortcuts import render
    from rest_framework import viewsets
    from rest_framework.pagination import PageNumberPagination
    from rest_framework.response import Response
    from rest_framework.views import APIView
    
    from user.models import User
    from user.serializers import UserSerializer, UserModelSerializer
    
    # 分頁(局部):自定義分頁器 局部
    class PageNum(PageNumberPagination):
        page_size = 4                           # 每頁顯示多少條
        page_size_query_param = 'page_size'     # 查詢字符串中代表每頁返回數據數量的參數名, 默認值: None
        page_query_param = 'page'               # 查詢字符串中代表頁碼的參數名, 有默認值: page
        max_page_size = None                    # 最大頁碼數限制
        
    
    class UserViewSet(viewsets.ModelViewSet):
        queryset = User.objects.all()
        serializer_class = UserModelSerializer
        filter_fields = {"username"}
        pagination_class = PageNum  # 注意不是列表(只能有一個分頁模式)
    
  • 前端代碼

    • Pagination.vue
    <template>
      <div>
        <a-pagination
          show-quick-jumper
          :default-current="2"
          :pageSize = '4'
          :total="count"
          show-less-items
          @change="onChange"
          v-model="current"
        />
          <!-- 一定是change方法,不然不能跳轉 -->
      </div>
    </template>
    
    <script>
    export default {
        props:[ 'count' ],
        data() {
            return {
                current:1
            }
        },
        methods: { 
            onChange() {
                this.$emit('getPage', this.current)
        },
        },
        created() {
    
        }
    }
    </script>
    
    • Index.vue
    <template>
    <div>
      <div id="components-layout-demo-basic">
            <a-layout-content>
                <TableList
                      :userListGet="userListGet"
                      :userList="userList"
                      @getUser="getUser"
                      @add="add"
                 >
                        
                 </TableList>
                 <Pagination
                      @getPage="getPage" 
                      :count="count"              
                  >
        		  </Pagination>
            </a-layout-content>
      </div>
    </div>
    </template>
    
    <script>
    import Pagination from "./components/Pagination"
    
    import { get_userlist } from '@/http/apis';
    export default {
        components:{
            BreadCrumb,
            TableList,
            Search,
            EditForm,
            Pagination
        },
        data() {
            return {
                userListGet:[],
                updateUserList:[],
                // 當前頁碼
                current:1,
                // 總共的數據多少條
                count:0
    
            }
        },
        methods: {
            getUser(){
              // 獲取用戶信息列表,父組件傳遞給子組件
              get_userlist(this.current).then(res=>{
                this.userListGet = res.results
                this.count = res.count
                console.log(this.count)
                console.log(this.userListGet)
              })
            },
            // 獲取頁碼
            getPage(currentChild){
              // 獲取到的currentChild是子組件傳遞過來是第幾頁
              this.current = currentChild
              console.log(this.current)
              this.getUser()
            }
        },
        created() {
    
        }
    }
    </script>
    
    <style scoped>
    #components-layout-demo-basic {
      text-align: center;
    }
    #components-layout-demo-basic .ant-layout-header,
    #components-layout-demo-basic .ant-layout-footer {
      background: white;
      color: #fff;
    }
    #components-layout-demo-basic .ant-layout-footer {
      line-height: 1.5;
    }
    #components-layout-demo-basic .ant-layout-content {
      background: white;
      color: #fff;
      min-height: 120px;
      line-height: 120px;
    }
    #components-layout-demo-basic > .ant-layout {
      margin-bottom: 48px;
    }
    #components-layout-demo-basic > .ant-layout:last-child {
      margin: 0;
    }
    </style>
    
    

33 利用數據解耦性完善查詢接口

export default {
    components:{
        BreadCrumb,
        TableList,
        Search,
        EditForm,
        Pagination
    },
    data() {
        return {
            roleListGet:[],
            searchList:{
                'zh_name':'',
                'page':1,
                'page_size':4,
                
            },

    },
methods: {
    find(){
              // 根據用戶名查找用戶信息
              search_for_role(this.searchList).then(res=>{
                  this.getRole()
                  // 數據解耦性!!!查詢和查詢某個其實可以調用同一個接口!
                  // 查詢所有:http://192.168.56.100:1594/?page=1&zh_name=
                  // 查詢某個:http://192.168.56.100:1594/?page=1&zh_name=many

              })
            },
    getRole(){
        this.searchList.page = this.current
        // 獲取用戶信息列表,父組件傳遞給子組件
        get_rolelist(this.searchList).then(res=>{
        this.roleListGet = res.results
        this.count = res.count
        console.log(this.count)
        console.log(this.roleListGet)
    })
    },
	}

34 序列化展示外鍵

  • 方法1 新增字段儲存名稱,新增字段為只讀,不影響同一序列化器的添加功能
class FlowConfSerializer(serializers.ModelSerializer):
    flowtype_name = serializers.ReadOnlyField(source='flowtype.name')
    # 第一種序列化展示方法,新增字段flowtype_name儲存名稱
    class Meta:
        model = FlowConf
        fields = "__all__"
        
'''
{
    "count": 3,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": 1,
            "flowtype_name": "財務工單",
            "name": "請假模板1",
            "description": "用於普通請假事宜",
            "customfield": "[{'field_name':'請假時間','type:'1'},{'field_name':'截止時間','type':'1'},{'field_name':'請假原由','type':'2'}]",
            "flowtype": 2
        }
    ]
}
'''
  • 方法2 替換字段為名稱,影響同一序列化器的添加功能
class FlowConfSerializer(serializers.ModelSerializer):
    flowtype = serializers.SerializerMethodField()
    class Meta:
        model = FlowConf
        fields = "__all__"
    def get_flowtype(self, row):
        return row.flowtype.name

'''
{
    "count": 3,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": 1,
            "flowtype": "財務工單",
            "name": "請假模板1",
            "description": "用於普通請假事宜",
            "customfield": "[{'field_name':'請假時間','type:'1'},{'field_name':'截止時間','type':'1'},{'field_name':'請假原由','type':'2'}]"
        }
    ]
}
'''

35 關於 action 屬性

35.1 詳解

'''
視圖集ViewSet
	繼承自APIView與ViewSetMixin
    作用也與APIView基本類似,提供了身份認證、權限校驗、流量管理等
	ViewSet主要通過繼承ViewSetMixin來實現在調用as_view()時傳入字典(如{‘get’:’list’})的映射處理工作
	在ViewSet中,沒有提供任何動作action方法,需要我們自己實現action方法
	
action的使用
	在視圖集中,除了上述默認的方法動作外,還可以添加自定義動作
	只要繼承了ViewSetMixin類,路由的配置就發生變化了,只需要寫映射即可,視圖類的方法中就會有個action

action的屬性
在視圖集中,我們可以通過action對象屬性來獲取當前請求視圖集時的action動作是哪個
'''
  • 實例分析
class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserModelSerializer
    
	def get_serializer_class(self):
        print('action------->', self.action)
        return UserModelSerializer

35.2 獲取某條數據

35.2.1 獲取某條數據
http://192.168.56.100:1594/user/user/1/
  • 返回數據
    • 獲取某一條數據就不是list類型
action-------> retrieve
35.2.2 獲取具體數據(查找)
http://192.168.56.100:1594/user/user/?username=魏魏最闊愛
  • 返回數據
    • 獲取到的哪怕一條數據,action動作都是list類型
action-------> list
35.2.3 添加數據
http://192.168.56.100:1594/user/user/
  • 返回數據
    • 添加時create
action-------> create

36 query和params

  • query和params傳參的區別

query傳參顯示參數,params傳參不顯示參數,params相對於query來說較安全一點

取值方法也有不同:query取值:this.$route.query.XXX || this.$route.params.xxx

query傳值頁面刷新數據還在,而params傳值頁面數據消失。

36.1 query

<!-- 傳參組件寫法 -->
<router-link :to="{path:'/home', query:{'pid':item.id}}"</router-link>

<!-- 寫在方法里 -->
jump(flowConf){
	this.$router.push(
		{
			path:'flowconfform',
			// 如果是path:'flowconfform/',多了/就會無法返回
			'query':{
			'name':flowConf.name,
			'id':flowConf.id
		}
	}
	)
}

36.2 params


37 循環之雙向綁定+添加自定義字段

  • 父組件
<template>
    <div>
        <div>
             <a-row>
            <a-col :span="19" style="height:500px">
                <CreateForm
                   :msg="flowConf" 
                ></CreateForm>
            </a-col>
            <a-col :span="5" style="height:500px;background:yellow">
                col-12
            </a-col>
            </a-row>
            <a-row>
            <a-col :span="24" style="height:160px">
                <Check
                    :flowConf="flowConf"
                ></Check>
            </a-col>
            </a-row>
        </div>
    </div>
</template>

<script>
import Check from '@/views/flowconfform-manage/components/Check'
import CreateForm from '@/views/flowconfform-manage/components/CreateForm'
export default {
    components:{
        Check,
        CreateForm
    },
    data() {
        return {
            flowConf:this.$route.query.flowconf,
        }
    },
    methods: {
        changeJson(){
            const textJson = this.flowConf.customfield
            console.log('自定義字段', textJson)
            const textJsonChange = JSON.parse(textJson)
            console.log('轉換后自定義字段', textJsonChange)
            this.flowConf.customfield = textJsonChange
            console.log(this.flowConf)
        }

    },
    created() {
    this.changeJson()
    }
}
</script>

<style scoped>

</style>
  • 子組件
<template>
    <div><h1>{{ msg.customfield }}</h1>
         <a-form-model :label-col="labelCol" :wrapper-col="wrapperCol">
            <a-form-model-item v-for="(select,item) in msg.customfield.field_list" :key="item">
            
            <p v-if="select.field_type==='input'" style="text-align:left">{{select.verbos_name}}:&emsp;
                <a-input v-model="select.value" style="width:200px" @change="giveValue"/>
            </p>
            
            <p v-show="select.field_type==='select'" style="text-align:left">
                {{select.verbos_name}}:&emsp;
                <a-select v-model="select.value" placeholder="please select your option" style="width:200px" @change="giveValue">
                    <a-select-option v-for="s in select.field_datasource" :key="s.value" :value="s.value">
                    {{ s.label }}
                    </a-select-option>
                </a-select>
            </p>

            <p v-show="select.field_type=='textarea'" style="text-align:left">
                {{select.verbos_name}}:
                <a-input v-model="select.value" type="textarea" style="width:700px;height:150px" @change="giveValue"/>
            </p>
            </a-form-model-item>
            <a-form-model-item :wrapper-col="{ span: 14, offset: 4 }">
                <a-button type="primary" @click="onSubmit">
                提交
                </a-button>
                <a-button style="margin-left: 10px;">
                取消
                </a-button>
            </a-form-model-item>
        </a-form-model>
    </div>
</template>
<script>
export default {
  props:[ 'msg' ],
  data() {
    return {  
      labelCol: { span: 4 },
      wrapperCol: { span: 14 },
      form: {
        name: '',
        region: undefined,
        date1: undefined,
        delivery: false,
        type: [],
        resource: '',
        desc: '',        
      },
      field_datasource:{},
    };
  },
  methods: {
    onSubmit() {
      console.log('submit!', this.form);
    },  
    // 老師的方法
    // giveValue(){
    //     for(var i in this.msg.customfield.form){
    //     const val = this.getInputValue(i);
    //     this.msg.customfield.form[i] = val;
    //   }       
    // },
    // getInputValue(key){
    //     for(var i=0;i<this.msg.customfield.field_list.length;i=i+1){
    //         const fileld_dic = this.msg.customfield.field_list[i];
    //         const k1 = fileld_dic['name'];
    //         if(key == k1){
    //         return fileld_dic['value']
    //     }
    //   }
    //   return ''
    // }
    giveValue(){
        const form_list = Object.keys(this.msg.customfield.form) 
        // 可以獲取到所有的key
        for(var i=0;i<this.msg.customfield.field_list.length;i++){
            form_list.forEach(item => {
                if(this.msg.customfield.field_list[i].name==item){
                    this.msg.customfield.form[item]=this.msg.customfield.field_list[i].value
                }
            });
        }
        return this.msg
    }
  },

};
</script>

<style scoped>
</style>

38 serializer之元祖類型怎么展示名字

38.1 models字段

class WorkOrderModel(BaseModel):
    status_choices = (
        ('1', '審批中'),
        ('2', '被駁回'),
        ('3', '完成')
    )
    order_status = models.CharField('工單狀態', help_text='審批中/被駁回/完成', choices=status_choices, default='1', max_length=30)

38.2 serializers寫法

class WorkOrderSerializer(serializers.ModelSerializer):
    order_status_name = serializers.SerializerMethodField(required=False)

    class Meta:
        model = WorkOrderModel
        fields = '__all__'

    def get_order_status_name(self, row):
        status_choices = dict(row.status_choices)
        return status_choices[row.order_status]

39 Vue前端json格式注意事項

  • 前端接收到的數據只有在是json字符串格式(雙引號)的情況下才能轉換成json格式!
  • 從數據庫獲取到的數據默認是單引號!

39.1 Vue把單引號變成雙引號的方法

const data = res.results              
var dic = data.replace(/'/g, '"')

39.2 把雙引號數據變成json格式

this.form = JSON.parse(dic)

40 后端常見問題(filter&get)

obj1 = FlowConf.objects.filter(id=1)			# 這是個列表(嵌套字典形式),是queryset對象
obj2 = FlowConf.objects.get(id=1)  				# 找不到會報錯,具體值
obj3 = FlowConf.objects.filter(id=1)[0]			# obj對象(字典形式,.name可以取出具體值)

41 前端常見問題(登錄不上)

  • 前端登錄不上
    • login頁面有問題:登錄成功沒有設置token(頁面就能查看)
    • main.js有問題:查看全局過濾配置,可能沒有設置token
    • http/index.js有問題:后端身份驗證失敗:錯誤token/token過期

42 VUE端v-for循環解決textarea框重復輸入的問題

<template>
   <div>
         <div v-for=" (self, item) in list" :key="item">                         
           <a-textarea
               v-model="value[item]"
           />
   </div>
</template>
<script>
    data() {
        return {
            value:[],
            list:[
                {'id':1},
                {'id':2}
            ]
        }
    },
</script>

43 自己寫的一個權限驗證

class SubOrderView(APIView):
    def get(self, request):
        token = request.META.get('HTTP_AUTHORIZATION')
        # 用戶id和角色
        user_info = jwt_decode_handler(token[4:])
        user_id = user_info.get('user_id')
        role_info = UserRole.objects.filter(user_id=user_id).values('role_id')
        role_list = []
        for i in role_info:
            role_list.append(i.get('role_id'))
        # 實例化子工單id
        suborder_id = request.query_params.get('id')
        # 審批類型id,1角色審批,2指定人審批
        approve_type_id = SubOrderModel.objects.filter(pk=suborder_id).first().approve_type_id
        if approve_type_id == '1':
            approve_userrole_id = SubOrderModel.objects.filter(pk=suborder_id).first().approve_userrole_id
            if approve_userrole_id in role_list:
                return Response({'msg': '有審批權限', 'code': 200, 'type': 1})
            return Response({'msg': '無審批權限', 'code': 200, 'type': 0})
        elif approve_type_id == '2':
            approve_user = SubOrderModel.objects.filter(pk=suborder_id).first().approve_user
            if user_id == approve_user:
                return Response({'msg': '有審批權限', 'code': 200, 'type': 1})
            return Response({'msg': '無審批權限', 'code': 200, 'type': 0})

44 JWT生成token

44.1 settings.py

"""
Django settings for opwf project.

Generated by 'django-admin startproject' using Django 2.0.13.

For more information on this file, see
https://docs.djangoproject.com/en/2.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.0/ref/settings/
"""
import datetime
import os, sys

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'uorj1ni^mnut@wo@c%)iv)%5=8dxlml4-j0!f3b%4#f*8a5)3t'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['*']


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'corsheaders',
    'user.apps.UserConfig',
    'workflow.apps.WorkflowConfig',
    'workerorder.apps.WorkerorderConfig',
    # 'jwt',
    # 'rest_framework_jwt',
    # 'rest_framework.authentication'

]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'corsheaders.middleware.CorsMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'opwf.urls'
CORS_ORIGIN_ALLOW_ALL = True

CORS_ORIGIN_WHITELIST = (
    'http://127.0.0.1:8080',
    'http://localhost:8080',
)
CORS_ALLOW_CREDENTIALS = True

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'opwf.wsgi.application'


# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'opwf_db',
        'USER': 'root',
        'PASSWORD': '1',
        'HOST': '127.0.0.1',
        'PORT': '3306'
    }
}


# Password validation
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators

REST_FRAMEWORK = {
    # 文檔報錯: AttributeError: ‘AutoSchema’ object has no attribute ‘get_link’
    # 用下面的設置可以解決
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
    # 默認設置是:
    # 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.openapi.AutoSchema',

    # 異常處理器
    # 'EXCEPTION_HANDLER': 'user.utils.exception_handler',

    # Base API policies      默認渲染器類
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ],
    # 默認解析器類
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser'
    ],
    # 1.認證器(全局)
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',  # 在 DRF中配置JWT認證
        # 'rest_framework.authentication.SessionAuthentication',  # 使用session時的認證器
        # 'rest_framework.authentication.BasicAuthentication'  # 提交表單時的認證器
    ],

    # 2.權限配置(全局): 順序靠上的嚴格
    'DEFAULT_PERMISSION_CLASSES': [
        # 'rest_framework.permissions.IsAdminUser',  # 管理員可以訪問
        # 'rest_framework.permissions.IsAuthenticated',  # 認證用戶可以訪問
        # 'rest_framework.permissions.IsAuthenticatedOrReadOnly',  # 認證用戶可以訪問, 否則只能讀取
        'rest_framework.permissions.AllowAny',  # 所有用戶都可以訪問
    ],
    # 3.限流(防爬蟲)
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle',
    ],
    # 3.1限流策略
    # 'DEFAULT_THROTTLE_RATES': {
    #     'user': '100/hour',  # 認證用戶每小時100次
    #     'anon': '300/day',  # 未認證用戶每天能訪問3次
    # },

    'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',
    'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
    'DEFAULT_VERSIONING_CLASS': None,

    # 4.分頁(全局):全局分頁器, 例如 省市區的數據自定義分頁器, 不需要分頁
    # 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    # # 每頁返回數量
    # 'PAGE_SIZE': 3,
    # 5.過濾器后端
    'DEFAULT_FILTER_BACKENDS': [
        'django_filters.rest_framework.DjangoFilterBackend',
        # 'django_filters.rest_framework.backends.DjangoFilterBackend', 包路徑有變化
    ],

    # 5.1過濾排序(全局):Filtering 過濾排序
    'SEARCH_PARAM': 'search',
    'ORDERING_PARAM': 'ordering',

    'NUM_PROXIES': None,

    # 6.版本控制:Versioning  接口版本控制
    'DEFAULT_VERSION': None,
    'ALLOWED_VERSIONS': None,
    'VERSION_PARAM': 'version',

    # Authentication  認證
    # 未認證用戶使用的用戶類型
    'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
    # 未認證用戶使用的Token值
    'UNAUTHENTICATED_TOKEN': None,

    # View configuration
    'VIEW_NAME_FUNCTION': 'rest_framework.views.get_view_name',
    'VIEW_DESCRIPTION_FUNCTION': 'rest_framework.views.get_view_description',

    'NON_FIELD_ERRORS_KEY': 'non_field_errors',

    # Testing
    'TEST_REQUEST_RENDERER_CLASSES': [
        'rest_framework.renderers.MultiPartRenderer',
        'rest_framework.renderers.JSONRenderer'
    ],
    'TEST_REQUEST_DEFAULT_FORMAT': 'multipart',

    # Hyperlink settings
    'URL_FORMAT_OVERRIDE': 'format',
    'FORMAT_SUFFIX_KWARG': 'format',
    'URL_FIELD_NAME': 'url',

    # Encoding
    'UNICODE_JSON': True,
    'COMPACT_JSON': True,
    'STRICT_JSON': True,
    'COERCE_DECIMAL_TO_STRING': True,
    'UPLOADED_FILES_USE_URL': True,

    # Browseable API
    'HTML_SELECT_CUTOFF': 1000,
    'HTML_SELECT_CUTOFF_TEXT': "More than {count} items...",

    # Schemas
    'SCHEMA_COERCE_PATH_PK': True,
    'SCHEMA_COERCE_METHOD_NAMES': {
        'retrieve': 'read',
        'destroy': 'delete'
    },

    # 'Access-Control-Allow-Origin':'http://localhost:8080',
    # 'Access-Control-Allow-Credentials': True

}

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/

LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/

STATIC_URL = '/static/'
AUTH_USER_MODEL = 'user.User'

# jwt載荷中的有效期設置
JWT_AUTH = {
    # 1.token前綴:headers中 Authorization 值的前綴
    'JWT_AUTH_HEADER_PREFIX': 'JWT',
    # 2.token有效期:一天有效
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
    # 3.刷新token:允許使用舊的token換新token
    'JWT_ALLOW_REFRESH': True,
    # 4.token有效期:token在24小時內過期, 可續期token
    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(hours=24),
    # 5.自定義JWT載荷信息:自定義返回格式,需要手工創建
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'user.utils.jwt_response_payload_handler',
}

44.2 utils.py

# -*- coding: utf-8 -*-
def jwt_response_payload_handler(token, user=None, request=None, role=None):
    """
        自定義jwt認證成功返回數據
        :token 返回的jwt
        :user 當前登錄的用戶信息[對象]
        :request 當前本次客戶端提交過來的數據
        :role 角色
    """
    if user.first_name:
        name = user.first_name
    else:
        name = user.username
        return {
            'authenticated': 'true',
             'id': user.id,
             "role": role,
             'name': name,
             'username': user.username,
             'email': user.email,
             'token': token,
        }

44.3 models.py

from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    '''
    username:用戶名
    password:密碼
    mobile:手機號
    email:郵箱
    '''
    username = models.CharField(max_length=30, unique=True)
    # 寫上 unique=True 就可以指定唯一,驗證字段的時候自動驗證
    password = models.CharField(max_length=256)
    mobile = models.CharField(max_length=11)
    email = models.CharField(max_length=30)
    token = models.CharField(max_length=256, default='')
    weixin = models.CharField(max_length=30, null=True)
    date_joined = models.DateField(auto_now_add=True)

    class Meta:
        db_table = 'user_user'
        verbose_name = '用戶表'
        verbose_name_plural = verbose_name

44.4 serializers.py

# -*- coding: utf-8 -*-
from rest_framework import serializers
from rest_framework_jwt.serializers import jwt_payload_handler
from rest_framework_jwt.settings import api_settings
from user.models import User
class UserSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    username = serializers.CharField()
    password = serializers.CharField()
    mobile = serializers.CharField()
    email = serializers.EmailField()
    weixin = serializers.CharField()
    token = serializers.CharField(read_only=True)

    def create(self, data):
        username = data.get('username', '')
        password = data.get('password', '')
        mobile = data.get('mobile', '')
        email = data.get('email', '')
        weixin = data.get('weixin', '')

        user = User(username=username, email=email, mobile=mobile, weixin=weixin)
        user.set_password(password)
        user.save()
        # 補充生成記錄登錄狀態的token
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        user.token = token
        return user

44.5 urls.py

from django.urls import path
from rest_framework.routers import DefaultRouter
from rest_framework_jwt.views import obtain_jwt_token

from user import views

router = DefaultRouter()
router.register(r'user', views.UserViewSet)
router.register(r'user_get', views.UserGetViewSet)
router.register(r'role', views.RoleViewSet)
router.register(r'role_get', views.RoleGetViewSet)

urlpatterns = [
    path('login/', obtain_jwt_token),
    path('register/', views.RegisterView.as_view()),
]

45 token解碼

# 如果是APIView獲取的,在VUE中定義的,可以使用以下方式解密

from rest_framework_jwt.utils import jwt_decode_handler

def get(self, request):
        token = request.META.get('HTTP_AUTHORIZATION')
        # 用戶id和角色
        user_info = jwt_decode_handler(token[4:])

46 serializer 關於查詢

  • 以工單表和工單子表為例

46.1 models.py

from django.db import models

# Create your models here.
from utils.MyBaseModel import BaseModel
from user.models import User
from workflow.models import FlowConf



class WorkOrderModel(BaseModel):
    '''
    flowconf:工單名稱(一對多,FlowConf)
    create_user:工單創建用戶(一對多,User表)
    create_ts:創建時間
    order_status:工單狀態(審批中/被駁回/完成)
    description:描述(text,存儲用戶工單描述)
    '''
    status_choices = (
        ('1', '審批中'),
        ('2', '被駁回'),
        ('3', '完成')
    )
    flowconf = models.ForeignKey(FlowConf, on_delete=models.CASCADE, null=True)
    create_user = models.ForeignKey(User, on_delete=models.CASCADE)
    order_status = models.CharField('工單狀態', help_text='審批中/被駁回/完成', choices=status_choices, default='1', max_length=30)
    parameter = models.TextField(default='{}')
    # description = models.TextField('描述', help_text='存儲用戶工單描述')

    class Meta:
        db_table = 'workerorder_workerorder'
        verbose_name = '實例化工單'
        verbose_name_plural = verbose_name



class SubOrderModel(BaseModel):
    '''
    mainorder:一對多(WorkOrder)實例化工單相連
    approve_user:一對多(內置User表)審批人
    approbe_user_role:審批角色
    approve_userrole_id:審批角色id
    sequence_number:審批序號
    approve_ts:審批時間
    action_status:審批狀態(待審批/通過/拒絕/退回)
    suborder_status:子任務狀態(待處理/已經處理/待上一節點處理)
    approve_text:審批意見
    approve_type_id: 審批類型
    '''
    action_status_choices = (
        ('1', '待審批'),
        ('2', '通過'),
        ('3', '拒絕'),
        ('4', '退回')
    )
    suborder_status_choices = (
        ('1', '待處理'),
        ('2', '已經處理'),
        ('3', '待上一節點處理')
    )
    mainorder = models.ForeignKey(WorkOrderModel, on_delete=models.CASCADE)
    approve_user = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL, default='')
    approbe_user_role = models.CharField('審批角色', max_length=30, null=True)
    approve_userrole_id = models.IntegerField('審批角色id')
    sequence_number = models.IntegerField('審批序號')

    approve_ts = models.DateTimeField('審批時間', auto_now_add=True)
    action_status = models.CharField('審批狀態', help_text='待審批/通過/拒絕/退回', choices=action_status_choices, default='1', max_length=30)
    suborder_status = models.CharField('子任務狀態', help_text='待處理/已經處理/待上一節點處理', choices=suborder_status_choices, default='1', max_length=30)
    approve_text = models.TextField('審批意見')
    type_choice = (
        ('1', '角色審批'),
        ('2', '指定人員審批')
    )
    approve_type_id = models.CharField(max_length=30, choices=type_choice)

    class Meta:
        db_table = 'workerorder_suborder'
        verbose_name = '實例化子工單'
        verbose_name_plural = verbose_name

46.2 serializer.py

from rest_framework import serializers
from workerorder.models import WorkOrderModel, SubOrderModel
from workflow.models import FlowConf


class WorkOrderSerializer(serializers.ModelSerializer):
    order_status_name = serializers.SerializerMethodField(required=False)
    create_user_name = serializers.SerializerMethodField(required=False)
    flowconf_name = serializers.CharField(required=False, source='flowconf.name')

    class Meta:
        model = WorkOrderModel
        fields = '__all__'

    def get_order_status_name(self, row):
        status_choices = dict(row.status_choices)
        return status_choices[row.order_status]

    def get_create_user_name(self, row):
        # print(type(row.create_user.username))
        # str
        return row.create_user.username


class SubOrderSerializer(serializers.ModelSerializer):
    approve_user_name = serializers.SerializerMethodField(required=False)
    action_status_name = serializers.SerializerMethodField(required=False)
    suborder_status_name = serializers.SerializerMethodField(required=False)
    flowconf_name = serializers.SerializerMethodField(required=False)
    class Meta:
        model = SubOrderModel
        fields = "__all__"

    def get_action_status_name(self, row):
        return dict(row.action_status_choices)[row.action_status]

    def get_suborder_status_name(self, row):
        return dict(row.suborder_status_choices)[row.suborder_status]

    def get_approve_user_name(self, row):
        if row.approve_user:
            return row.approve_user.username
        else:
            return ''
    def get_flowconf_name(self, row):
        return row.mainorder.flowconf.name

47 Model字段類型

47.1 常用字段類型

from django.db import models

class UserGroup(models.Model):
    uid = models.AutoField(primary_key=True)

    name = models.CharField(max_length=32,null=True, blank=True)
    email = models.EmailField(max_length=32)
    text = models.TextField()

    ctime = models.DateTimeField(auto_now_add=True)      # 只有添加時才會更新時間
    uptime = models.DateTimeField(auto_now=True)         # 只要修改就會更新時間

    ip1 = models.IPAddressField()                  # 字符串類型,Django Admin以及ModelForm中提供驗證 IPV4 機制
    ip2 = models.GenericIPAddressField()           # 字符串類型,Django Admin以及ModelForm中提供驗證 Ipv4和Ipv6

    active = models.BooleanField(default=True)

    data01 = models.DateTimeField()                      # 日期+時間格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ
    data02 = models.DateField()                          # 日期格式      YYYY-MM-DD
    data03 = models.TimeField()                          # 時間格式      HH:MM[:ss[.uuuuuu]]

    age = models.PositiveIntegerField()           # 正小整數 0 ~ 32767
    balance = models.SmallIntegerField()          # 小整數 -32768 ~ 32767
    money = models.PositiveIntegerField()         # 正整數 0 ~ 2147483647
    bignum = models.BigIntegerField()             # 長整型(有符號的) -9223372036854775808 ~ 9223372036854775807
    
    user_type_choices = (
        (1, "超級用戶"),
        (2, "普通用戶"),
        (3, "普普通用戶"),
    )
    user_type_id = models.IntegerField(choices=user_type_choices, default=1)

47.2 不常用字段類型

URLField(CharField)
        - 字符串類型,Django Admin以及ModelForm中提供驗證 URL

    SlugField(CharField)
        - 字符串類型,Django Admin以及ModelForm中提供驗證支持 字母、數字、下划線、連接符(減號)

    CommaSeparatedIntegerField(CharField)
        - 字符串類型,格式必須為逗號分割的數字

    UUIDField(Field)
        - 字符串類型,Django Admin以及ModelForm中提供對UUID格式的驗證

    FilePathField(Field)
        - 字符串,Django Admin以及ModelForm中提供讀取文件夾下文件的功能
        - 參數:
                path,                      文件夾路徑
                match=None,                正則匹配
                recursive=False,           遞歸下面的文件夾
                allow_files=True,          允許文件
                allow_folders=False,       允許文件夾

    FileField(Field)
        - 字符串,路徑保存在數據庫,文件上傳到指定目錄
        - 參數:
            upload_to = ""      上傳文件的保存路徑
            storage = None      存儲組件,默認django.core.files.storage.FileSystemStorage

    ImageField(FileField)
        - 字符串,路徑保存在數據庫,文件上傳到指定目錄
        - 參數:
            upload_to = ""      上傳文件的保存路徑
            storage = None      存儲組件,默認django.core.files.storage.FileSystemStorage
            width_field=None,   上傳圖片的高度保存的數據庫字段名(字符串)
            height_field=None   上傳圖片的寬度保存的數據庫字段名(字符串)


    DurationField(Field)
        - 長整數,時間間隔,數據庫中按照bigint存儲,ORM中獲取的值為datetime.timedelta類型

    FloatField(Field)
        - 浮點型

    DecimalField(Field)
        - 10進制小數
        - 參數:
            max_digits,小數總長度
            decimal_places,小數位長度

    BinaryField(Field)
        - 二進制類型

48 一對多ForeignKey可選參數

1、to,                                          # 要進行關聯的表名
2、to_field=None,                               # 要關聯的表中的字段名稱
3、on_delete=None,                              # 當刪除關聯表中的數據時,當前表與其關聯的行的行為
            - models.CASCADE                    # ,刪除關聯數據,與之關聯也刪除
            - models.DO_NOTHING                 # ,刪除關聯數據,引發錯誤IntegrityError
            - models.PROTECT                    # ,刪除關聯數據,引發錯誤ProtectedError
            - models.SET_NULL                   # ,刪除關聯數據,與之關聯的值設置為null(前提FK字段需要設置為可空)
            - models.SET_DEFAULT                # ,刪除關聯數據,與之關聯的值設置為默認值(前提FK字段需要設置默認值)
            - models.SET                        # ,刪除關聯數據,
4、related_name=None,                           # 反向操作時,使用的字段名,用於代替 【表名_set】 如: obj.表名_set.all()
                                                # 在做自關聯時必須指定此字段,防止查找沖突
5、delated_query_name=None,                     # 反向操作時,使用的連接前綴,用於替換【表名】
                                                # 如:models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
6、limit_choices_to=None,                       # 在Admin或ModelForm中顯示關聯數據時,提供的條件:
            - limit_choices_to={'nid__gt': 5}
            - limit_choices_to=lambda : {'nid__gt': 5}
7、db_constraint=True                           # 是否在數據庫中創建外鍵約束
8、parent_link=False                            # 在Admin中是否顯示關聯數據
一對多ForeignKey可選參數

49 Django 一對多表結構操作

49.1 一對多基本增刪改查

  • models.py
from django.db import models

class UserInfo(models.Model):
    name = models.CharField(max_length=64,unique=True)
    ut = models.ForeignKey(to='UserType')

class UserType(models.Model):
    type_name = models.CharField(max_length=64,unique=True)

  • views.py
from django.shortcuts import render,HttpResponse
from app01 import models

def orm(request):
    # 1 創建
    # 創建數據方法一
    models.UserInfo.objects.create(name='root', ut_id=2)
    # 創建數據方法二
    obj = models.UserInfo(name='root', ut_id=2)
    obj.save()
    # 創建數據庫方法三(傳入字典必須在字典前加兩個星號)
    dic = {'name': 'root', 'ut_id': 2}
    models.UserInfo.objects.create(**dic)

    # 2 刪除
    # models.UserInfo.objects.all().delete()  # 刪除所有
    models.UserInfo.objects.filter(name='root').delete()  # 刪除指定

    # 3 更新
    # models.UserInfo.objects.all().update(ut_id=1)
    # models.UserInfo.objects.filter(name='zhangsan').update(ut_id=4)

    # 4.1 正向查找 user_obj.ut.type_name
    print( models.UserInfo.objects.get(name='zhangsan').ut.type_name )
    print( models.UserInfo.objects.filter(ut__type_name='student') )

    # 4.2 反向查找 type_obj.userinfo_set.all()
    print( models.UserType.objects.get(type_name='student').userinfo_set.all() )
    print( models.UserType.objects.get(type_name='student').userinfo_set.filter(name='zhangsan') )

    return HttpResponse('orm')

views.py

49.2 一對多更多查詢

  • 一對多創建表
from django.db import models

class UserType(models.Model):
    user_type_name = models.CharField(max_length=32)
    def __str__(self):
        return self.user_type_name            #只有加上這個,Django admin才會顯示表名

class User(models.Model):
    username = models.CharField(max_length=32)
    pwd = models.CharField(max_length=64)
    ut = models.ForeignKey(
        to='UserType',
        to_field='id',

        # 1、反向操作時,使用的連接前綴,用於替換【表名】
        # 如: models.UserGroup.objects.filter(a__字段名=1).values('a__字段名')
        related_query_name='a',

        #2、反向操作時,使用的字段名,用於代替 【表名_set】 如: obj.b_set.all()
        # 使用時查找報錯
        # related_name='b',
    )
  • 一對多正向反向查找
from django.shortcuts import HttpResponse
from app01 import models

def orm(request):
    # 1 正向查找
    #1.1 正向查找user表用戶名
    print(models.User.objects.get(username='zhangsan').username)           # zhangsan

    #1.2 正向跨表查找用戶類型
    print(models.User.objects.get(username='zhangsan').ut.user_type_name)  # student

    #1.3 雙下划線正向跨表正向查找
    print( models.User.objects.all().values('ut__user_type_name','username') )


    # 2 反向查找
    # 2.1:【表名_set】,反向查找user表中用戶類型為student 的所有用戶
    print( models.UserType.objects.get(user_type_name='student').user_set.all() )           # [<User: lisi>, <User: wangwu>]

    # 2.2:【a__字段名】反向查找user表中張三在UserType表中的類型:([<UserType: teacher>])
    print( models.UserType.objects.filter(a__username='zhangsan') )                         # student
    # 這里的a是user表的ForeignKey字段的參數:related_query_name='a'

    # 2.3: 雙下划線跨表反向查找
    print( models.UserType.objects.all().values('a__username', 'user_type_name') )


    # 3 自動創建User表和UserType表中的數據
    '''
    username = [{'username':'zhangsan','pwd':'123','ut_id':'1'},
                {'username':'lisi','pwd':'123','ut_id':'1'},
                {'username':'wangwu','pwd':'123','ut_id':'1'},]

    user_type = [{'user_type_name':'student'},{'user_type_name':'teacher'},]

    for type_dic in user_type:
        models.UserType.objects.create(**type_dic)

    for user_dic in username:
        models.User.objects.create(**user_dic)
    '''
    return HttpResponse('orm')
  • 一對多使用values和values_list結合雙下划線跨表查詢
from django.shortcuts import HttpResponse
from app01 import models

def orm(request):
    # 第一種:values-----獲取的內部是字典  拿固定列數
    # 1.1 正向查找: 使用ForeignKey字段名ut結合雙下划線查詢
    models.User.objects.filter(username='zhangsan').values('username', 'ut__user_type_name')

    # 1.2 向查找: 使用ForeignKey的related_query_name='a',的字段
    models.UserType.objects.all().values('user_type_name', 'a__username')


    # 第二種:values_list-----獲取的是元組  拿固定列數
    # 1.1 正向查找: 使用ForeignKey字段名ut結合雙下划線查詢
    stus = models.User.objects.filter(username='zhangsan').values_list('username', 'ut__user_type_name')

    # 1.2 反向查找: 使用ForeignKey的related_query_name='a',的字段
    utype = models.UserType.objects.all().values_list('user_type_name', 'a__username')


    # 3 自動創建User表和UserType表中的數據
    '''
    username = [{'username':'zhangsan','pwd':'123','ut_id':'1'},
                {'username':'lisi','pwd':'123','ut_id':'1'},
                {'username':'wangwu','pwd':'123','ut_id':'1'},]

    user_type = [{'user_type_name':'student'},{'user_type_name':'teacher'},]

    for type_dic in user_type:
        models.UserType.objects.create(**type_dic)

    for user_dic in username:
        models.User.objects.create(**user_dic)
    '''

    return HttpResponse('orm')

50 Django 多對多表結構操作

50.1 創建並操作多對多表

50.1.1 m2m字段創建
  • 自己不創建第三張關系表,有m2m字段: 根據queryset對象增刪改查(推薦)
from django.shortcuts import HttpResponse
from app01 import models

def orm(request):
    user_info_obj = models.UserInfo.objects.get(username='zhangsan')
    user_info_objs = models.UserInfo.objects.all()

    group_obj = models.UserGroup.objects.get(group_name='group_python')
    group_objs = models.UserGroup.objects.all()

    # 添加: 正向
    group_obj.user_info.add(user_info_obj)
    group_obj.user_info.add(*user_info_objs)
    # 刪除:正向
    group_obj.user_info.remove(user_info_obj)
    group_obj.user_info.remove(*user_info_objs)

    # 添加: 反向
    user_info_obj.usergroup_set.add(group_obj)
    user_info_obj.usergroup_set.add(*group_objs)
    # 刪除:反向
    user_info_obj.usergroup_set.remove(group_obj)
    user_info_obj.usergroup_set.remove(*group_objs)

    # 查找:正向
    print(group_obj.user_info.all())                                # 查找group_python組中所有用戶
    print(group_obj.user_info.all().filter(username='zhangsan'))
    # 查找:反向
    print(user_info_obj.usergroup_set.all())                        # 查找用戶zhangsan屬於那些組
    print(user_info_obj.usergroup_set.all().filter(group_name='group_python'))


    # 雙下划線 正向、反向查找
    # 正向:從用戶組表中查找zhangsan屬於哪個用戶組:[<UserGroup: group_python>]
    print( models.UserGroup.objects.filter(user_info__username='zhangsan'))

    # 反向:從用戶表中查詢group_python組中有哪些用戶:related_query_name='m2m'
    print( models.UserInfo.objects.filter(m2m__group_name='group_python'))


    # 自動創建UserInfo表和UserGroup表中的數據
    '''
    user_list = [{'username':'zhangsan'},
                {'username':'lisi'},
                {'username':'wangwu'},]
    group_list = [{'group_name':'group_python'},
               {'group_name':'group_linux'},
               {'group_name':'group_mysql'},]

    for c in user_list:
        models.UserInfo.objects.create(**c)

    for l in group_list:
        models.UserGroup.objects.create(**l)
    '''

    return HttpResponse('orm')
50.1.2 手動創建第三張表
from django.db import models

#表1:主機表
class Host(models.Model):
    nid = models.AutoField(primary_key=True)
    hostname = models.CharField(max_length=32,db_index=True)

#表2:應用表
class Application(models.Model):
    name = models.CharField(max_length=32)

#表3:自定義第三張關聯表
class HostToApp(models.Model):
    hobj = models.ForeignKey(to="Host",to_field="nid")
    aobj = models.ForeignKey(to='Application',to_field='id')

# 向第三張表插入數據,建立多對多外鍵關聯
HostToApp.objects.create(hobj_id=1,aobj_id=2)
50.1.3 m2m創建(id查詢)
  • 自己不創建第三張關系表,有m2m字段,根據數字id增刪改查
from django.db import models

class Host(models.Model):
    hostname = models.CharField(max_length=32,db_index=True)

class Group(models.Model):
    group_name = models.CharField(max_length=32)
    m2m = models.ManyToManyField("Host")

    
  • 查詢
from django.shortcuts import HttpResponse
from app01 import models

def orm(request):
    # 使用間接方法對第三張表操作
    obj = models.Group.objects.get(id=1)

    # 1、添加
    obj.m2m.add(1)           # 在第三張表中增加一個條目(1,1)
    obj.m2m.add(2, 3)        # 在第三張表中增加條目(1,2)(1,3)兩條關系
    obj.m2m.add(*[1,3])        # 在第三張表中增加條目(1,2)(1,3)兩條關系

    # 2、刪除
    obj.m2m.remove(1)             # 刪除第三張表中的(1,1)條目
    obj.m2m.remove(2, 3)          # 刪除第三張表中的(1,2)(1,3)條目
    obj.m2m.remove(*[1, 2, 3])    # 刪除第三張表中的(1,1)(1,2)(1,3)條目

    # 3、清空
    obj.m2m.clear()                 # 刪除第三張表中application條目等於1的所有條目

    # 4 更新
    obj.m2m.set([1, 2,])             # 第三張表中會刪除所有條目,然后創建(1,1)(1,2)條目

    # 5 查找
    print( obj.m2m.all() )           # 等價於 models.UserInfo.objects.all()

    # 6 反向查找: 雙下划線
    hosts = models.Group.objects.filter(m2m__id=1)         # 在Host表中id=1的主機同時屬於那些組


    # 自動創建Host表和Group表中的數據
    '''
    hostname = [{'hostname':'zhangsan'},
                {'hostname':'lisi'},
                {'hostname':'wangwu'},]
    group_name = [{'group_name':'DBA'},{'group_name':'public'},]

    for h in hostname:
        models.Host.objects.create(**h)
    for u in group_name:
        models.Group.objects.create(**u)
    '''

    return HttpResponse('orm')

50.2 values和values_list結合雙下划線跨表查詢

from django.shortcuts import HttpResponse
from app01 import models

def orm(request):
    # 第一種:values-----獲取的內部是字典,拿固定列數
    # 1.1 正向查找: 使用ManyToManyField字段名user_info結合雙下划線查詢
    models.UserGroup.objects.filter(group_name='group_python').values('group_name', 'user_info__username')

    # 1.2 反向查找: 使用ManyToManyField的related_query_name='m2m',的字段
    models.UserInfo.objects.filter(username='zhangsan').values('username', 'm2m__group_name')


    # 第二種:values_list-----獲取的是元組  拿固定列數
    # 2.1 正向查找: 使用ManyToManyField字段名user_info結合雙下划線查詢
    models.UserGroup.objects.filter(group_name='group_python').values_list('group_name', 'user_info__username')

    # 2.2 反向查找: 使用ManyToManyField的related_query_name='m2m',的字段
    lesson = models.UserInfo.objects.filter(username='zhangsan').values_list('username', 'm2m__group_name')



    # 自動創建UserInfo表和UserGroup表中的數據
    '''
    # user_info_obj = models.UserInfo.objects.get(username='lisi')
    # user_info_objs = models.UserInfo.objects.all()
    #
    # group_obj = models.UserGroup.objects.get(group_name='group_python')
    # group_objs = models.UserGroup.objects.all()
    #
    # group_obj.user_info.add(*user_info_objs)
    # user_info_obj.usergroup_set.add(*group_objs)

    user_list = [{'username':'zhangsan'},
                {'username':'lisi'},
                {'username':'wangwu'},]
    group_list = [{'group_name':'group_python'},
               {'group_name':'group_linux'},
               {'group_name':'group_mysql'},]

    for c in user_list:
        models.UserInfo.objects.create(**c)

    for l in group_list:
        models.UserGroup.objects.create(**l)
    '''

    return HttpResponse('orm')

50.3 多對多時ManyToManyField可以添加的參數

1、to,                        # 要進行關聯的表名
2、related_name=None,         # 反向操作時,使用的字段名,用於代替 【表名_set】如: obj.表名_set.all()
3、related_query_name=None,   # 反向操作時,使用的連接前綴,用於替換【表名】     
                              # 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
4、limit_choices_to=None,     # 在Admin或ModelForm中顯示關聯數據時,提供的條件:
                              # - limit_choices_to={'nid__gt': 5}
5、symmetrical=None,          # 用於多對多自關聯,symmetrical用於指定內部是否創建反向操作字段
6、through=None,              # 自定義第三張表時,使用字段用於指定關系表
7、through_fields=None,       # 自定義第三張表時,使用字段用於指定關系表中那些字段做多對多關系表
8、db_constraint=True,        # 是否在數據庫中創建外鍵約束
   db_table=None,             # 默認創建第三張表時,數據庫中表的名稱


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM