在Django中通常一個功能的開發順序為:創建應用 -> 數據庫表字段設計 -> 視圖函數編寫 -> 應用路由 -> 全局路由 -> 測試
Django的工作流程是:
一、創建用戶應用
打開 Tools > Run manage.py Task…,運行 startapp空格加應用名稱
startapp users # startapp:創建應用的關鍵字,user:為應用的名字
生成應用后一定要記得將應用名加入到settings.py文件里名為INSTALLED_APPS的列表中,遵循規范加入到列表的最后,再添加應用也一樣
# settings.py INSTALLED_APPS=[ ……, 'users', ]
應用目錄詳解
users ├── migrations # 存放遷移數據表的歷史文件,內容自動生成 │ └── __init__.py # 一個空文件,告訴 Python 該目錄是一個 Python 包。 ├── __init__.py # 一個空文件,告訴 Python 該目錄是一個 Python 包。 ├── admin.py # 該應用的后台管理系統配置,這里用不到 ├── apps.py # 該應用的一些配置,這里用不到 ├── models.py # 數據模塊,用來定義數據表結構 ├── tests.py # Django提供的自動化測試功能,這里用不到 └── views.py # 視圖模塊,代碼邏輯處理的主要地點,項目中大部分代碼均在這里編寫
二、編寫數據模型
創建時間、修改時間和邏輯刪除這些字段會經常用到,每次都寫代碼會很冗余,解決辦法就是寫一個類繼承使用
1.在項目跟目錄下創建utils目錄,再在utils目錄下創建base_models.py文件
1 # utils/base_models.py 2 from django.db import models 3 4 5 class BaseModel(models.Model): 6 """ 7 數據庫表公共字段 8 """ 9 create_time = models.DateTimeField(auto_now_add=True, verbose_name='創建時間', help_text='創建時間') 10 update_time = models.DateTimeField(auto_now=True, verbose_name='更新時間', help_text='更新時間') 11 is_delete = models.BooleanField(default=False, verbose_name='邏輯刪除', help_text='邏輯刪除') 12 13 class Meta: 14 # abstract = True:為抽象模型類,用於其他模型類繼承,數據庫遷移時不會創建ModelBase表 15 abstract = True 16 verbose_name = '公共字段表' 17 db_table = 'BaseModel'
2.編寫用戶表的模型,繼承上面的類
1 #users/models.py 2 from django.db import models 3 4 from utils.base_models import BaseModel 5 6 7 class Users(BaseModel): 8 id = models.AutoField(primary_key=True, verbose_name='id主鍵') 9 username = models.CharField(max_length=50, unique=True, verbose_name='用戶名') 10 password = models.CharField(max_length=50, verbose_name='密碼') 11 email = models.EmailField(max_length=50, verbose_name='郵箱') 12 13 def __str__(self): 14 return self.username 15 16 class Meta: 17 db_table = 'lx_users' 18 verbose_name = '用戶數據' 19 verbose_name_plural = verbose_name
遷移數據庫,打開 Tools > Run manage.py Task…,依次行
makemigrations
migrate
三、編寫視圖函數
1.先把需要用的token認證機制寫出來,token 這里使用Python中的 pyjwt 庫
1.1 安裝 pip install pyjwt
pip install pyjwt
1.2 生成token,在users目錄下邊創建generate_token.py文件
1 # users/generate_token.py 2 from datetime import datetime, timedelta 3 import jwt 4 5 from django.conf import settings 6 7 def generate_jwt_token(username): 8 token = jwt.encode({ 9 'exp': datetime.utcnow() + timedelta(days=1), 10 'iat': datetime.utcnow(), 11 'data': { 12 'username': username 13 } 14 }, settings.SECRET_KEY, algorithm='HS256') 15 return token.decode('utf-8')
1.3 認證token的裝飾器,在utils目錄下創建jwt_permission_required.py文件
1 # utils/jwt_permission_required.py 2 import jwt 3 from django.conf import settings 4 from django.http import JsonResponse 5 from users.models import Users 6 7 8 def auth_permission_required(func): 9 def decorator(view_func): 10 def _wrapped_view(request, *args, **kwargs): 11 try: 12 if func == "func": 13 auth = request.META.get('HTTP_AUTHORIZATION').split() 14 else: 15 auth = request.request.META['HTTP_AUTHORIZATION'].split() 16 except AttributeError: 17 return JsonResponse({"status_code": 401, "message": "沒有權限"}) 18 if auth[0].lower() == 'token': 19 try: 20 dict = jwt.decode(auth[1], settings.SECRET_KEY, algorithms=['HS256']) 21 username = dict.get('data').get('username') 22 except jwt.ExpiredSignatureError: 23 return JsonResponse({"status_code": 401, "message": "token 已過期"}) 24 except jwt.InvalidTokenError: 25 return JsonResponse({"status_code": 401, "message": "token 無效"}) 26 except Exception as e: 27 return JsonResponse({"status_code": 401, "message": "無法獲取用戶對象"}) 28 try: 29 Users.objects.get(username=username) 30 except Users.DoesNotExist: 31 return JsonResponse({"status_code": 401, "message": "用戶不存在"}) 32 else: 33 return JsonResponse({"status_code": 401, "message": "不支持身份驗證類型"}) 34 return view_func(request, *args, **kwargs) 35 return _wrapped_view 36 return decorator
2. 返回的參數格式需要統一規范,在utils目錄下創建common.py文件來放一些公共配置,寫一個result字典,所有返回格式都用這個
1 # utils/common.py 2 result = { 3 "message": None, 4 "success": False, 5 "details": None 6 }
3.表單校驗,在users目錄下創建forms.py文件,用來校驗用戶注冊時輸入的數據
1 # users/forms.py 2 import re 3 4 from django import forms 5 from django.core.exceptions import ValidationError 6 7 from users.models import Users 8 9 10 class RegisterForm(forms.Form): 11 username = forms.CharField( 12 label="用戶名", 13 required=True, 14 max_length=50, 15 min_length=2, 16 error_messages={ 17 "required": "用戶名不能為空", 18 "max_length": "用戶名最長不能超過50個字符", 19 "min_length": "用戶名最小長度為2" 20 }) 21 password = forms.CharField( 22 label="密碼", 23 required=True, 24 max_length=50, 25 min_length=5, 26 error_messages={ 27 "required": "密碼不能為空", 28 "max_length": "密碼最長不能超過50個字符", 29 "min_length": "密碼最小長度為5" 30 }) 31 r_password = forms.CharField( 32 required=True, 33 max_length=50, 34 min_length=5, 35 label="確認密碼", 36 error_messages={ 37 "required": "確認密碼不能為空", 38 "max_length": "確認密碼最長不能超過50個字符", 39 "min_length": "確認密碼最小長度為5" 40 }) 41 email = forms.CharField( 42 min_length=5, 43 required=True, 44 label="郵箱", 45 error_messages={ 46 "required": "郵箱不能為空", 47 "max_length": "郵箱最長不能超過50個字符", 48 "min_length": "郵箱最小長度為5" 49 } 50 ) 51 52 def clean_username(self): 53 val = self.cleaned_data.get("username") 54 ret = Users.objects.filter(username=val) 55 if not ret: 56 return val 57 else: 58 raise ValidationError("該用戶名已注冊!") 59 60 def clean_email(self): 61 val = self.cleaned_data.get("email") 62 if re.match(r'^[0-9a-zA-Z_]{0,19}@[0-9a-zA-Z]{1,13}\.[com,cn,net]{1,3}$', val): 63 return val 64 else: 65 raise ValidationError("郵箱格式不正確!") 66 67 # 走完所有的校驗才走clean 68 def clean(self): 69 pwd = self.cleaned_data.get("password") 70 r_pwd = self.cleaned_data.get("r_password") 71 if pwd and r_pwd: 72 if pwd != r_pwd: 73 raise forms.ValidationError("兩次密碼不一致") 74 return self.cleaned_data
4.編寫用戶應用的視圖函數
1 # users/views.py 2 from django.http import JsonResponse 3 from django.views import View 4 5 from users.forms import RegisterForm 6 from users.generate_token import generate_jwt_token 7 from users.models import Users 8 from utils.jwt_permission_required import auth_permission_required 9 from utils.common import result 10 11 12 class LoginView(View): 13 def post(self, request): 14 result["message"] = "登錄失敗" 15 result["success"] = False 16 result["details"] = None 17 json_data = request.body.decode('utf-8') 18 if json_data: 19 python_data = eval(json_data) 20 username = python_data.get('username') 21 password = python_data.get('password') 22 data = Users.objects.filter(username=username, password=password).values("id", "username").first() 23 if data: 24 token = generate_jwt_token(username) 25 result["message"] = "登錄成功" 26 result["success"] = True 27 result["details"] = {"id": data["id"], "username": data["username"],"token": token} 28 return JsonResponse(result, status=200) 29 result["details"] = "用戶名或密碼錯誤" 30 return JsonResponse(result, status=400) 31 return JsonResponse(result, status=500) 32 33 34 class RegisterView(View): 35 def post(self, request): 36 result["message"] = "注冊失敗" 37 result["success"] = False 38 result["details"] = None 39 json_data = request.body.decode('utf-8') 40 if json_data: 41 python_data = eval(json_data) 42 data = RegisterForm(python_data) 43 if data.is_valid(): 44 data.cleaned_data.pop("r_password") 45 Users.objects.create(**data.cleaned_data) 46 data.cleaned_data.pop("password") 47 result["message"] = "注冊成功" 48 result["success"] = True 49 result["details"] = data.cleaned_data 50 return JsonResponse(result, status=200) 51 else: 52 result["details"] = data.errors 53 return JsonResponse(result, status=400) 54 return JsonResponse(result, status=500) 55 56 @auth_permission_required("func") 57 def demo(request): 58 if request.method == 'GET': 59 return JsonResponse({"state": 1, "message": "token有效"}) 60 else: 61 return JsonResponse({"state": 0, "message": "token無效"})
四、編寫應用的路由
在users目錄下創建urls.py文件
1 #users/urls.py 2 from django.urls import path 3 4 from users import views 5 6 urlpatterns = [ 7 path('login', views.LoginView.as_view()), 8 path('register', views.RegisterView.as_view()), 9 path('demo', views.demo) 10 ]
五、定義全局路由
1 # testplatform/urls.py 2 from django.contrib import admin 3 from django.urls import path, include 4 5 urlpatterns = [ 6 path('admin/', admin.site.urls), 7 path('api/user/', include("users.urls")), 8 ]
六、測試
1.注冊接口
2.登錄接口
3.token驗證接口
3.1 不帶token
3.2 帶token
GitHub持續更新:地址https://github.com/debugf/testplatform
轉載請注明出處,商用請征得作者本人同意,謝謝!!!