240219_Django Ninja

Haoliang Tang Lv3

本来是打算仔细学FastAPI作为主力后端框架的,后来又意外发现了Django Ninja。api的写法和FastAPI很相似。

基于Django,是一个Fast Django REST Framework,能利用Django的ORM等生态。于是决定目前重点学Django Ninja作为主力后端。

環境構築

1
pip install django-ninja

当然django本身作为依赖是要安装的 pip install Django

创建项目,运行项目和Django一样:

创建项目:

1
django-admin startproject xxx

创建一个应用:

1
python manage.py startapp xxx

完了后记得去settings.py的INSTALLED_APPS下追加startapp的app

启动后端服务器:(默认8000端口)

1
python manage.py runserver

更换服务器的监听端口,请使用命令行参数

1
python manage.py runserver 8080

设置TIME_ZONE

1
2
3
4
TIME_ZONE = 'Asia/Shanghai'
TIME_ZONE = 'Asia/Tokyo'

USE_TZ = False # 数据库里的时间就不是UTC了,而是配置的时区

runserver后命令行显示的时间应该就是本地时区了

创建管理员:(可以登录管理员后台:8000/admin)

1
python manage.py createsuperuser

(Username: admin Password: chedishibai)

startapp创建的app目录下编辑admin.py追加models后,可以登录管理员后台,在界面上crud

https://docs.djangoproject.com/zh-hans/5.0/intro/overview/

https://docs.djangoproject.com/zh-hans/5.0/intro/tutorial02/#introducing-the-django-admin

在数据库中创建表

1
python manage.py migrate

migrate命令只会为在INSTALLED_APPS里声明了的应用进行数据库迁移

根据检测到的模型变化创建新的迁移(反正就是改变了models就要makemigrations)

1
python manage.py makemigrations

改变模型需要这三步:

Django的Python shell交互

1
python manage.py shell

然后可以手动插入一条记录之类的

https://docs.djangoproject.com/en/5.0/intro/tutorial02/#playing-with-the-api

apiの書き方

startproject的project目录下(app目录下也可以)创建个api.py文件。(对,放弃使用views.py了,views.py本质是返回渲染的html模板,前后端分离后这个views.py文件也变得鸡肋了)

导入NinjaAPI类:

1
2
3
4
5
6
7
8
9
10
from ninja import NinjaAPI

api = NinjaAPI()
# xxx_api = NinjaAPI() # app目录下也用NinjaAPI()的话,这样防止重名

# 或者app的用Router()实例化
# xxx_api = Router()
# 那么项目目录的根api.py需要add_router app (在项目的urls.py里写)
# from xxx.api import xxx_api
# api.add_router("/app名字xxx/", xxx_api)

项目目录的urls.py追加:

1
2
3
4
5
6
7
8
9
10
from django.contrib import admin
from django.urls import path
from 项目名称.api import api
#from app名称.api import xxx_api

urlpatterns = [
path('admin/', admin.site.urls),
path("api/", api.urls), # path("", api.urls),
#path("xxx/", xxx_api.urls), # app是用NinjaAPI()实例化的话这里追加;如果是用Router()实例化则在项目目录的api.py追加add_router(或者urls.py这里都import了写这也行,感觉このやり方より良い)
]

注意: 后端定义api的url最后面slashを付いた方が良い

如果@recommendation_api.get("/recommend"),前端发的请求的url最后要是有”/“,那服务端响应不了,Not Found 404

@recommendation_api.get("/recommend/")(最后slashを付いた),不管前端发的请求的url最后有没有slash,服务端都能响应

有了Django Ninja, 处理endpoint函数会自动做serialization,把return的python dict转成(serialize to)json. (应该是decorator做的)

(DRF djangorestframework是需要serializer操作的)

CRUD官网例子:https://django-ninja.dev/tutorial/other/crud/

1
2
3
4
@api.post("/employees")
def create_employee(request, payload: EmployeeIn):
employee = Employee.objects.create(**payload.dict())
return {"id": employee.id}

post例子中**payload.dict()两个星是把hash map{}给destructure成keyword arguments以便传给create()

JSON转python dict也是django-ninja框架自动实现的,定义了schema(说是Schema其实是检查request body有没有缺字段)的话,如此处的EmployeeIn

但定义Schema是必要的(什么xxxIn, xxxOut),不然报错TypeError: Object of type QuerySet is not JSON serializable

自定义错误信息,特别是request body不匹配时,ninja的Schema会自动报错

https://stackoverflow.com/questions/74086447/how-to-customize-the-validation-error-using-django-ninja

https://django-ninja.dev/guides/errors/

手撸request, response

https://docs.djangoproject.com/en/5.0/ref/request-response/

https://www.jianshu.com/p/94785f71fdd8

https://stackoverflow.com/questions/35059916/how-to-change-status-of-jsonresponse-in-django#:~:text=JsonResponse%20normally%20returns%20HTTP%20200%2C%20which%20is%20the,a%20subclass%20of%20HttpResponse%3A%20response%20%3D%20JsonResponse%28%7B%27status%27%3A%27false%27%2C%27message%27%3Amessage%7D%2C%20status%3D500%29

见我PFN的oa题

models ORM 数据库

学数据库的时候,就感觉到一张表的schema就像定义一个类,一行记录就像一个类的实例

models定义的class属性不需要写id,migrate时Django会自动产生一个id (除非指定了作为pk *primary_key*=True)

参考Django文档 https://docs.djangoproject.com/zh-hans/5.0/intro/tutorial02/

各种字段类型 https://docs.djangoproject.com/zh-hans/5.0/ref/models/fields/#field-types

https://docs.djangoproject.com/en/5.1/topics/db/queries/

各种ORM方法 https://docs.djangoproject.com/en/5.1/ref/models/querysets/

自定义User Model

from django.contrib.auth.models import AbstractUser

https://cloud.tencent.com/developer/article/1743076

https://cloud.tencent.com/developer/article/1358567

https://stackoverflow.com/questions/21514354/abstractuser-vs-abstractbaseuser-in-django

连MariaDB/MySQL

官网文档 How the documentation is organized -> Reference guides去找 Databases

https://docs.djangoproject.com/en/5.0/ref/databases/#mysql-notes


安装Python的MySQL客户端

https://pypi.org/project/mysqlclient/ 官方文档推荐的

mysqlclient is a native driver. It’s the recommended choice.

1
2
sudo pacman -S python-mysqlclient
pip install mysqlclient

MariaDB/MySQL先要创建数据库

1
2
MariaDB [(none)]> create database arxiv;
MariaDB [(none)]> GRANT ALL PRIVILEGES ON arxiv.* TO 'hltang'@'localhost';

连接数据库

修改settings.py的DATABASES

1
2
3
4
5
6
7
8
9
10
DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"NAME": "arxiv",
"USER": "",
"PASSWORD": "",
"HOST": "127.0.0.1",
"PORT": "3306",
}
}

然后迁移 python manage.py migrate ,MariaDB里就有表了

或者使用配置文件

1
2
3
4
5
6
7
8
DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"OPTIONS": {
"read_default_file": BASE_DIR / "mariadb.cnf",
},
}
}

根目录(和db.sqlite3同级)创建个mariadb.cnf文件

1
2
3
4
5
6
7
[client]
database = "arxiv"
user = ""
password = ""
default-character-set = utf8
host = "127.0.0.1"
port = "3306"

但这样直接migrate会报错,参考: https://zhuanlan.zhihu.com/p/488566846

使用PyMySQL, pip install PyMySQL ,项目目录 __init__.py 中写入:

1
2
import pymysql
pymysql.install_as_MySQLdb()

然后再migrate就可以了

ORM的各种操作 objects.xxx()

从官网文档 The model layer 去看

https://docs.djangoproject.com/en/5.0/topics/db/queries/

https://docs.djangoproject.com/en/5.0/ref/models/querysets/

Django ORM常用操作介绍

sqlmigrate 命令接收一个迁移的名称 polls/migrations/0001_initial.py ,然后返回对应的 SQL:

1
python manage.py sqlmigrate polls 0001

查看SQL语句,确实主键(id)会被自动创建 (settings.py或apps.py中的DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField')

sqlmigrate命令并没有真正在你的数据库中执行迁移 - 相反,它只是把命令输出到屏幕上,让你看看Django认为需要执行哪些SQL语句

插入假数据:

1
pip install Faker
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import random
from faker import Faker
from django.utils import timezone
from books.models import Author, Book

fake = Faker()

# 生成3个作者
for _ in range(3):
Author.objects.create(
name=fake.name(),
birthdate=fake.date_of_birth()
)

#authors = Author.objects.all()

# 生成20本书
for _ in range(20):
Book.objects.create(
title=fake.sentence(nb_words=4, variable_nb_words=True, ext_word_list=None),
description=fake.paragraph(nb_sentences=3, variable_nb_sentences=True, ext_word_list=None),
author=random.choice(authors),
isbn=fake.isbn13(separator="-")
)

print("假数据插入完成!")

Faker库往数据库里插入假数据。注意不能用python generate_fake_data.py运行会报错导入不了model。

需要在django shell里运行

1
python manage.py shell

然后粘贴Enter,数据库里有了

CORS

https://github.com/adamchainz/django-cors-headers

Response Header里应该有access-control-allow-origin字段ようになった

注意 CORS_ALLOW_CREDENTIALS = True , 不然cookie不会被发到不同源的前端

Pagination 分页

https://django-ninja.dev/guides/response/pagination/

登录注册&权限

https://docs.djangoproject.com/ja/5.0/topics/auth/default/

https://docs.djangoproject.com/zh-hans/5.0/topics/auth/customizing/

用自带的 from django.contrib.auth import authenticate, login, logout

Django自带的auth登录认证 django.contrib.auth里自带方法的使用,还有AbstractUser (注意settings.py里追加AUTH_USER_MODEL)

https://zhuanlan.zhihu.com/p/677674023 手撸版,但login仅仅只是展示username,没有涉及session,token

https://www.bilibili.com/video/BV1SW4y1F7gT?p=52&vd_source=0ae70a0cf4a652e3c632b9150cd2f90e 手撸版,很赞

https://zhuanlan.zhihu.com/p/377145935 手撸版自定义

JWT

Django API Authentication using JWT Tokens 用的是DRF(djangorestframework),==很有启发性==,包括用户模型应该继承自AbstractUser (还有postman里看cookie,才意识到问题是cookie没写到浏览器里,然后axios cookie一搜,axios跨域默认不带cookie)

Django User 模块之 AbstractUser 扩展详解 AbstractUser (注意settings.py里追加AUTH_USER_MODEL)

参考

https://django-ninja.dev

DjangoCon 2022 | Introducing Django Ninja DjangoCon Europe上作者发表,作者是🇺🇦人。注意看对Schema类的介绍,不需要再自己做类型转换了,request body的json直接转成了python dict。Schema像是对request/response做类型检查以及过滤余計的数据 (说是Schema其实是数据库里的视图View)

CRUD with Django Ninja 来自官方油管,作者自ら解説。跟着做library项目,思路流程清晰。19min左右学到了Schema里再套Schema,比如Book表只存了author_id,但想要一起显示author的其他属性

Creating a CRUD API with Django-Ninja 这位油管主讲得也很明白

https://www.bugbytes.io/posts/django-ninja/

Django-Ninja APIs - Modern API Development in Django 涉及到Schema和django-ninja-extra库

Django Ninja Ecommerce Inventory Integration

Django Ninja CRUD | Managing Form data | Image Uploads 最后有上传文件

  • Title: 240219_Django Ninja
  • Author: Haoliang Tang
  • Created at : 2024-02-19 00:00:00
  • Updated at : 2025-04-29 23:36:33
  • Link: https://hl-tang.github.io/2024/02/19/240219_Django Ninja/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments