起因
以往在做 Web 后端的开发的时候,我大多使用 C# 的 ASP.NET Core 或者一些别的技术栈。
这不,最近来了个活,使用 Django 做后端开发,所以得先来了解了解 Django。
得益于在其他技术栈上的经验,我不需要从头开始理解这个技术栈中的每个概念,可以找到其中相近的概念,就地迁移。
一些基础概念
概念 | 在 Django 中 |
---|---|
Router(路由) |
在 Django 中使用一个名为 同时 DRF 中提供 |
Controller(控制器) |
Django 中的控制器称为 View(虽说是 View,但不要和 MVC 里的 View 搞混),页面返回、API响应都是靠这个。 View 可由一个接受 同时,DRF 中还有如 ViewSet 这些概念。 |
Middleware(中间件) |
Django 同样也有中间件,与上面讲到的控制器接近一样,不多阐述。 |
ORM |
Django 自带 ORM ( |
Model(数据模型) |
一个继承 |
Depencency Inject(依赖注入) |
Django 没有自带依赖注入,Python 作为弱类型语言也不好做依赖注入,当然也不好做 Service Layer。 |
细节 / 小抄
下列例程取自项目模板、各种在线技术文档
Router (urls)
例子
使用
django.urls
from django.urls import path
from . import views
urlpatterns = [
path("articles/2003/", views.special_case_2003),
path("articles/<int:year>/", views.year_archive),
path("articles/<int:year>/<int:month>/", views.month_archive),
path("articles/<int:year>/<int:month>/<slug:slug>/", views.article_detail),
]
使用 DRF
from rest_framework import routers
router = routers.SimpleRouter()
router.register(r'users', UserViewSet)
router.register(r'accounts', AccountViewSet)
urlpatterns = router.urls
说明
urls.py
导出 urlpatterns
数组变量,其成员可包括如 django.urls
的:
-
path(route, view, kwargs=None, name=None)
设置 route 对应的 Controller / View,kwargs 将传入 view 指定的函数. -
re_path(route, view, kwargs=None, name=None)
与path(...)
相同,但是 route 参数使用正则表达式. -
include(module, namespace=None)
include(pattern_list)
include((pattern_list, app_namespace), namespace=None)
从其他模块引入它们的urlpatterns
.
Controller (View)
例子
有关 View 的使用
from django.http import HttpResponse, HttpResponseNotFound, Http404
from django.views.decorators.http import require_http_methods
from django.shortcuts import render, redirect
import datetime
@require_http_methods(["GET", "POST"])
def current_datetime(request):
now = datetime.datetime.now()
html = '<html lang="en"><body>It is now %s.</body></html>' % now
return HttpResponse(html)
def my_view(request):
# ...
if foo:
return HttpResponseNotFound("<h1>Page not found</h1>")
else:
return HttpResponse("<h1>Page was found</h1>")
def my_view2(request):
# ...
# Return a "created" (201) response code.
return HttpResponse(status=201)
def detail(request, poll_id):
try:
p = ...
except Poll.DoesNotExist:
raise Http404("Poll does not exist")
return render(request, "polls/detail.html", { ... })
Middleware
例子
有关中间件的使用
def simple_middleware(get_response):
# One-time configuration and initialization.
def middleware(request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
return middleware
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
ORM / Model
例子
模型的定义
from django.db import models
class Person(models.Model):
SHIRT_SIZES = {
"S": "Small",
"M": "Medium",
"L": "Large",
}
name = models.CharField(max_length=60)
shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
class Runner(models.Model):
MedalType = models.TextChoices("MedalType", "GOLD SILVER BRONZE")
name = models.CharField(max_length=60)
medal = models.CharField(blank=True, choices=MedalType, max_length=10)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through="Membership")
def __str__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
class Meta:
constraints = [
models.UniqueConstraint(
fields=["person", "group"], name="unique_person_group"
)
]
模型增删查改
# 增
p = Person(name="Fred Flintstone", shirt_size="L")
p.save()
beatles = Group.objects.create(name="The Beatles")
beatles.members.add(john, through_defaults={"date_joined": date(1960, 8, 1)})
# 删
e.delete()
# 查
somewhere = p.shirt_size
Group.objects.filter(members__name__startswith="Paul")
# 改
Entry.objects.filter(pub_date__year=2007).update(headline="Everything is the same")
说明
需要告诉 Django 你会使用这些模型,例如模型定义在 之后便可通过 有关
INSTALLED_APPS
配置和 Migrationsxxx.models
,那么需要在 settings.py
中的 INSTALLED_APPS
加入模块,如:INSTALLED_APPS = [
"xxx", # 无需 .models
# ...
]
manage.py migrate
或者 manage.py makemigrations
执行 Migrations。
在 在进行 Migrate 等操作时添加 多数据库使用
settings.py
中进行配置:DATABASES = {
"default": {
"NAME": "app_data",
"ENGINE": "django.db.backends.postgresql",
"USER": "postgres_user",
"PASSWORD": "s3krit",
},
"users": {
"NAME": "user_data",
"ENGINE": "django.db.backends.mysql",
"USER": "mysql_user",
"PASSWORD": "priv4te",
},
}
--database=users
参数指定对哪个数据库进行操作# 保存时使用 kwargs 传入 using 选择数据库
p.save(using="second", ...)
# 查询时使用 using(...) 方法
User.objects.using("legacy_users").get(username="fred")
数据库路由决定哪个 Model 要不要在某个数据库上进行配置, 那么在 多数据库路由
在 settings.py
中进行配置:DATABASE_ROUTERS = ["path.to.AuthRouter", "path.to.PrimaryReplicaRouter"]
class AuthRouter:
"""
A router to control all database operations on models in the
auth and contenttypes applications.
"""
route_app_labels = {"auth", "contenttypes"}
def db_for_read(self, model, **hints):
"""
Attempts to read auth and contenttypes models go to auth_db.
"""
if model._meta.app_label in self.route_app_labels:
return "auth_db"
return None
def db_for_write(self, model, **hints):
"""
Attempts to write auth and contenttypes models go to auth_db.
"""
if model._meta.app_label in self.route_app_labels:
return "auth_db"
return None
def allow_relation(self, obj1, obj2, **hints):
"""
Allow relations if a model in the auth or contenttypes apps is
involved.
"""
if (
obj1._meta.app_label in self.route_app_labels
or obj2._meta.app_label in self.route_app_labels
):
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""
Make sure the auth and contenttypes apps only appear in the
'auth_db' database.
"""
if app_label in self.route_app_labels:
return db == "auth_db"
return None
class Meta
中带有 app_label = "auth"
的模型将会走 auth_db
如 filter(...)
等查询可能是 lazy 的,在读取时才会求值。
其他功能(点击跳转原文档):模型元数据(Meta)、模型继承、数据库事务
上次修改於 2025-10-01