Skip to content
Go back

Django 项目启动指南

Edit page

创建项目

使用 PyCharm 选择 Django 项目模板创建

修改时区与语言

LANGUAGE_CODE = "zh-hans"
TIME_ZONE = "Asia/Shanghai"projectname/settings.py

配置数据库

MySQL 为例

为项目写一个配置文件config.yaml

DATABASE:
  ENGINE: django.db.backends.mysql
  USER: username
  PASSWORD: password
  HOST: 192.168.0.123
  NAME: database_name
  PORT: 3306
  OPTIONS:
    charset: utf8mb4config.yaml

settings.py 中引入配置文件

import yaml

with open("config.yaml", 'r') as f:
    CONFIG = yaml.safe_load(f)

DATABASES = {
    "default": CONFIG['DATABASE']
}projectname/settings.py

安装mysqlclient驱动

uv add mysqlclient

检查配置是否成功

python manage.py check

这个命令会检查整个项目的配置,包括数据库连接等。如果一切正常,它会显示System check identified no issues (0 silenced).

即使一切正常,也别着急执行 python manage.py migrate,因为如果你需要自定义 User Model,这个操作需要在第一次迁移数据库之前做,否则你需要在创建了自定义 User Model 之后删掉数据库重新迁移。

mysqlclient 是官方推荐的驱动,底层是C语言,效率较高,但是需要安装C依赖;如果遇到困难,可以使用PyMySQL驱动,它是纯Python实现的,能确保成功。

如果你使用PyMySQL驱动连接MySQL,你需要

uv add pymysql

然后添加代码

import pymysql

pymysql.version_info = (1, 4, 3, "final", 0)
pymysql.install_as_MySQLdb()projectname/__init__.py

创建 App

这里以创建一个名为account的 App 为例,我一般习惯把系统里的账户相关的功能都放在这里。

python manage.py startapp account

创建了 App 之后需要注册才会生效

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'account',  # 添加这一行,注册新创建的 App
]projectname/settings.py

自定义 User / Model 创建方法

Django 里面一个 Model 就对应一个数据库表,它有强大的 ORM 框架,你不需要自己去写 SQL。

Django 有自带的完善的用户系统,有默认的 User Model,但是如果你需要自定义用户模型(比如添加额外的字段),你需要创建一个继承自AbstractUser的自定义 User Model。

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

class User(AbstractUser):
    realname = models.CharField(max_length=128)
    idcard = models.CharField(max_length=18)
    phone_number = models.CharField(max_length=32)
    # 你可以添加更多字段account/models.py

指定自定义 User Model

AUTH_USER_MODEL = 'account.User'projectname/settings.py

写好了一个 Model 之后,就可以进行数据库迁移

python manage.py makemigrations
python manage.py migrate

写一个注册接口(view)

from rest_framework.decorators import api_view
from django.contrib.auth import get_user_model
from utils.decorators import validate_request
from utils.response import response_util
from utils.constants import ErrorCode

from account.request_serializer import RegisterSerializer

User = get_user_model()

@api_view(["POST"])
@validate_request(RegisterSerializer)
def register(request, data):
    if User.objects.filter(username=data.get("username")):
        return response_util(ErrorCode.USERNAME_ALREADY_REGISTERED)
    User.objects.create_user(**data)
    return response_util(ErrorCode.SUCCESS)account/views.py

这里面用到了我好多个包装好的工具,这是我长期实践总结出来的一些东西,下面一一列出来

@api_view

首先作为一个纯后端项目,建议使用@api_view,它是 Django REST Framework (DRF) 提供的装饰器,相较于@require_http_methods要强大的多

你需要安装它

uv add djangorestframework

Serializer 序列化器校验传入参数

我通过rest_framework.serializers序列化器和自己编写的validate_request装饰器,可以快速地校验传入参数的合规性,代码如下

from rest_framework import serializers

class RegisterSerializer(serializers.Serializer):
    username = serializers.CharField(max_length=128)
    password = serializers.CharField(max_length=128)account/request_serializer.py

这个序列化器功能强大,可以写很多复杂的校验逻辑

from functools import wraps


from utils.constants import ErrorCode
from utils.response import response_util


def validate_request(serializer_class):
    def decorator(view_func):
        @wraps(view_func)
        def wrapped_view(request, *args, **kwargs):
            if request.method == "GET":
                serializer = serializer_class(data=request.GET)
            else:
                serializer = serializer_class(data=request.data)
            if serializer.is_valid():
                return view_func(
                    request, data=serializer.validated_data, *args, **kwargs
                )
            else:
                return response_util(
                    ErrorCode.INVALID_PARAM, f"invalid data: {serializer.errors}"
                )

        return wrapped_view

    return decoratorutils/decorators.py

这个自己写的装饰器可以快速使用上面的序列化器,并且把不管是POST还是GET的参数都存放到data参数中,方面使用

返回数据封装

from rest_framework.response import Response
from rest_framework import status
from utils.constants import ErrorCode


def response_util(errno: ErrorCode, data=None) -> Response:
    if data is None:
        data = {}
    return Response({"errno": errno.value, "data": data}, status=status.HTTP_200_OK)utils/response.py
from enum import Enum


class ErrorCode(Enum):
    SUCCESS = 0
    #  其他错误代码utils/constants.py

通过这样的封装,所有的接口的返回都是一样的格式

{
    "errno": 0,
    "data": {}
}

注册路由

首先需要在app目录中创建一个urls.py

from django.urls import path

from account import views

urlpatterns = [
    path('register/', views.register),
]account/urls.py

然后再项目总urls.py中引入

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path("account/", include('account.urls')),
]projectname/urls.py

这样,就可以请求http://127.0.0.1:8000/account/register/

关于登录

登录接口编写

from django.contrib.auth import login, authenticate

@api_view(['POST'])
@validate_request(LoginSerializer)
def login_view(request, data):
    user = authenticate(**data)
    if user is None:
        return response_util(ErrorCode.PASSWORD_ERROR)
    login(request, user)
    return response_util(ErrorCode.SUCCESS, UserSerializer(user).data)account/views.py

这里用到了返回数据的序列化UserSerializer,这个也是自己写的,用来将model的数据转换成字典,方便返回。这个序列化器很强大,可以自定义一些需要计算的字段,可以排除某些字段等等。

from rest_framework.serializers import ModelSerializer
from django.contrib.auth import get_user_model

User = get_user_model()


class UserSerializer(ModelSerializer):
    class Meta:
        model = User
        fields = ["id", "username"]account/serializer.py

这个登录接口被调用后,会在Response Cookie中存储一个名为csrftoken的字段,这个字段是Django自带的一个CSRF防护机制,同时也是用户的token。

前端应该将其存储起来,调用其他需要登录的接口的时候,就需要将它放在请求头中,字段名为X-CSRFToken

如果你在使用 Apifox 或者 Postman 这些工具测试接口,你应该给登录这个接口添加一个提取变量的后置操作,将csrftoken提取到环境变量中,然后设置一个全局参数,在请求头中添加X-CSRFToken: {{csrftoken}}

如果你想强制清除登录状态,你可以清空 Cookie。

如果你要在其他接口校验当前请求的用户有没有登录,我写了如下修饰器

def login_required(view_func):
    @wraps(view_func)
    def wrapper(request, *args, **kwargs):
        if not request.user.is_authenticated:
            return response_util(ErrorCode.NOT_LOGGED_IN)
        return view_func(request, *args, **kwargs)

    return wrapperutils/decorators.py

把它放在需要登录的接口上,比如这个登出接口

@api_view(['GET'])
@login_required
def logout_view(request):
    logout(request)
    return response_util(ErrorCode.SUCCESS)account/views.py

我知道 Django 有一个自带的登录校验装饰器@login_required,但是它适合在前后端不分离项目中使用。它会强制重定向到登录页面,不能自定义返回内容,在前后端分离项目中并不合适。

TODO

Model各种类型字段,序列化,序列化器内置函数,级联序列化,异步定时任务,文件系统,生产环境部署运维……


Edit page
Share this post on:

Next Post
FastAPI 学习笔记