django快速上手教程
教程说明
本教程是基于python3.13、django5.2、djangorestframework3.16版本。
由于前后端分离已经是行业默认标准,本教程会结合django-rest-framework的第三方框架进行统一讲解。
本文字教程是不适合完全没有接触django的同学进行学习,可以利用网上其他入门教程搭配学习。
其他文档:
awesome-django
techempower web框架性能排行榜
Benchmarker web框架性能排行榜
新建项目
# 在当前目录创建一个django项目
uv init demo-django --python 3.13
cd demo-django
# https://www.django-rest-framework.org/#installation
uv add django Pillow django-redis
uv add psycopg[binary] mysqlclient
uv add oss2
# 生成django项目(前面选项是配置目录名称,后面选项是项目根目录名称)
.\.venv\Scripts\activate.ps1 # Windows激活虚拟环境
source .venv/Scripts/activate # Linux激活虚拟环境
django-admin startproject conf . # 当前目录新建django工程
# 创建一个app
python manage.py startapp users
python manage.py startapp news
# 创建其他目录和文件
touch cron_tasks.py
touch Dockerfile
mkdir apps
mkdir static
mkdir templates
mkdir -p utils/app
touch utils/app/storage.py请将所有app拖入apps文件夹中,并且在Pycharm中将apps文件夹标记为源代码根目录。
[project]
name = "demo-django"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = "==3.13.*"
dependencies = [
"django>=5.2.8",
"django-filter>=25.2",
"django-guardian>=3.2.0",
"django-redis>=6.0.0",
"djangorestframework>=3.16.1",
"markdown>=3.10",
"mysqlclient>=2.2.7",
"oss2>=2.19.1",
"pygments>=2.19.2",
"pillow>=12.0.0", # ImageField字段依赖
]
[dependency-groups]
dev = []
[project.scripts]
runserver = "manage:runserver"
[tool.uv]
index-url = "https://mirrors.aliyun.com/pypi/simple/""""cron 计划任务
开启命令:
echo "* * * * * root python3 ${PROJECT_PATH}/manage.py shell -c 'import cron_tasks; cron_tasks.main_tasks()' >> /proc/1/fd/1 2>&1" >> /etc/crontab
"""
import datetime
def minutely_tasks():
"""每分钟执行一次"""
pass
def five_minutely_tasks():
"""每5分钟执行一次"""
pass
def twenty_minutely_tasks():
"""每20分钟执行一次"""
pass
def hourly_tasks():
"""每小时执行一次"""
pass
def eight_hourly_tasks():
"""每8小时执行一次"""
pass
def daily_tasks():
"""每天执行一次"""
pass
def weekly_tasks():
"""每周执行一次"""
pass
def monthly_tasks():
"""每月执行一次"""
pass
def quarterly_tasks():
"""每个季度执行一次"""
pass
def yearly_tasks():
"""每年执行一次"""
pass
def main_tasks():
current_time = datetime.datetime.now()
# 执行每分钟的任务
minutely_tasks()
if current_time.minute % 5 == 0: # 每五分钟执行(5的倍数)
five_minutely_tasks()
if current_time.minute % 20 == 0: # 每20分钟执行(20的倍数)
twenty_minutely_tasks()
if current_time.minute == 0: # 每小时执行(每小时的第一分钟)
hourly_tasks()
if current_time.hour % 8 == 0 and current_time.minute == 0: # 每8小时执行(8的倍数)
eight_hourly_tasks()
if current_time.hour == 0 and current_time.minute == 0: # 每天的第一分钟执行
daily_tasks()
if current_time.weekday() == 0 and current_time.hour == 0 and current_time.minute == 0: # 每周的第一天开始时间(周一)
weekly_tasks()
if current_time.day == 1 and current_time.hour == 0 and current_time.minute == 0: # 每月的第一天
monthly_tasks()
if current_time.month in [1, 4, 7, 10] and current_time.day == 1 and current_time.hour == 0 and current_time.minute == 0: # 每个季度的第一天
quarterly_tasks()
if current_time.month == 1 and current_time.day == 1 and current_time.hour == 0 and current_time.minute == 0: # 每年的第一分钟
yearly_tasks()FROM registry.cn-hangzhou.aliyuncs.com/lieying/python:3.13
LABEL authors="xionglilong"
USER root
# 设置python环境变量
ENV PYTHONUNBUFFERED=1
ENV DJANGO_SETTINGS_MODULE=conf.settings
# 设置为生产环境
ENV PRODUCTION=1
# 设置项目路径
ENV PROJECT_PATH=/var/www/project/
# 启动端口
ENV PROJECT_PORT=19034
# 安装redis和cron
RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources
RUN apt-get update && \
apt-get install -y redis-server cron && \
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# 在容器内创建项目根目录
RUN mkdir -p ${PROJECT_PATH}
# 设置容器内工作目录,类似于cd命令
WORKDIR ${PROJECT_PATH}
RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
# 安装gunicorn
RUN pip install uv gunicorn gevent uvicorn
# 复制当前目录文件到容器项目根目录
ADD . ${PROJECT_PATH}
# 安装项目依赖包
RUN uv export --no-hashes --no-managed-python --verbose > requirements.txt
RUN pip install -r requirements.txt
# 创建cron任务
RUN echo "* * * * * root python ${PROJECT_PATH}/manage.py shell -c 'import cron_tasks; cron_tasks.main_tasks()' >> /proc/1/fd/1 2>&1" >> /etc/crontab
# 测试使用
#CMD python manage.py runserver 0:${PROJECT_PORT}
# asgi异步生产环境
#CMD gunicorn --worker-connections=1000 --workers=$((`grep processor /proc/cpuinfo | wc -l`*2+1)) --worker-class uvicorn.workers.UvicornWorker --worker-tmp-dir /dev/shm -b 0.0.0.0:${PROJECT_PORT} --access-logfile - conf.asgi
# wsgi同步生产环境
CMD service cron start && \
service redis-server start && \
gunicorn --worker-connections=1000 --workers=5 --worker-tmp-dir /dev/shm -b 0.0.0.0:${PROJECT_PORT} --access-logfile - conf.wsgiimport sys, os
# 将apps文件夹加入python搜索路径,可以直接import`apps`内的应用
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
DEBUG = True if not os.getenv("PRODUCTION") else False
ALLOWED_HOSTS = ['*']
INSTALLED_APPS = {
# ...
'users.apps.UsersConfig',
'news.apps.NewsConfig',
}
TEMPLATES = [
{
'DIRS': [os.path.join(BASE_DIR, 'templates')],
},
]
LANGUAGE_CODE = 'zh-hans' # 语言为中文
TIME_ZONE = 'Asia/Shanghai' # 时区是上海
USE_TZ = False # 不使用时区功能,关闭UTC时间(国内项目使用本地时间即可)
STATIC_URL = 'static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static'),]
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
MEDIA_URL = 'media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
FILE_UPLOAD_MAX_MEMORY_SIZE = 50 * 1024 * 1024 # 50MB,默认在内存处理的最大大小为2.5M
django.contrib.sessions: 会话(sessionid),对request,找到cookie中sessionid键的值在数据库中查询并解密出用户信息,放入request.user中。对response拦截,主动加上sessionid。USE_I18N: 18表示Internationlization这个单词首字母I和结尾字母N之间的字母有18个。I18N就是Internationlization的意思。
开发时,请将静态文件放在app下面的static目录下面再新建一个app名称的目录,不要放在根目录的static下,因为django默认不会搜索根目录下static目录的静态文件。
from django.contrib import admin
from django.conf import settings
from django.urls import path, re_path, include
from django.contrib.staticfiles import views as static_views
from django.views.static import serve as static_serve
from django.views.generic.base import RedirectView
admin.site.site_header = '登录页标题'
admin.site.site_title = '页面title字段标题'
urlpatterns = [
path('admin/', admin.site.urls),
path('news/', include('news.urls')),
path('users/', include('users.urls')),
path('static/<path:path>', static_views.serve, {'insecure': True}), # 在正式环境也可使用本地静态文件
path("media/<path:path>", static_serve, {'document_root': settings.MEDIA_ROOT}),
path('favicon.ico', RedirectView.as_view(url=r'static/favicon.ico')), # 收藏夹图标,图标demo: https://www.logosc.cn/logo/favicon
]
# urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # 这里配置的media,在生产环境无法使用。import os
import sys
from pathlib import Path
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
# 将apps文件夹加入环境变量(考虑到下面在加载模块时,还为导入settings配置)
BASE_DIR = Path(__file__).resolve().parent.parent
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
from .routing import websocket_urlpatterns # 导入你刚才创建的routing.py
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'conf.settings')
# 使用ProtocolTypeRouter来同时处理HTTP和WebSocket协议
application = ProtocolTypeRouter({
"http": get_asgi_application(), # 保持原有的HTTP处理方式
"websocket": AuthMiddlewareStack(URLRouter(websocket_urlpatterns)),
"lifespan": None, # 关闭 lifespan 协议,指进程启动后或关闭时的事件触发。(因为Uvicorn启动有提示所以尝试关闭掉,不过也没有效果)
})django项目根目录名称建议小写字母加中划线分割。可以直接改根目录文件夹名称。根目录名称随便改,与django项目内容无关。
django项目内包和文件模块名称必须用小写字母加下划线分割。
app命名:首先考虑想构造的URL访问地址命名你的APP名字(与URL一致),其次考虑主model。最好用一个复数的单词来命名。
一般项目里应该有个usersapp,来管理平台用户的登录注册功能。
djangorestframework 配置
Django REST framework 中文文档
Django REST framework 官方文档
drf-spectacular 官方文档
Simple JWT 官方文档
uv add djangorestframework markdown django-filter Pygments django-guardian djangorestframework-simplejwt drf-spectacular
INSTALLED_APPS = [
# ...
"django_filters", # 添加过滤器功能
"rest_framework",
"drf_spectacular", # API文档支持
]
# rest_framework的全局配置
REST_FRAMEWORK = {
'DATETIME_FORMAT': "%Y-%m-%d %H:%M:%S", # 序列化器日期时间格式配置
'DATE_FORMAT': "%Y-%m-%d", # 序列化器时间格式
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', # 注册API文档
# 分页功能的全局设置
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', # 修改默认的分页类型,使用每页数量和页面编号来分页
'PAGE_SIZE': 20, # 每个页面的数量,配置完后立马有分页功能,并且json数据格式会调整,还会多出几个字段:count、next、previous,文件路径字段还会添加完整域名
}
# 自定义用户模型(用户模型在后面会配置)
# AUTH_USER_MODEL = "users.UserModel"
# AUTHENTICATION_BACKENDS = ('users.views.CustomAuthenticationBackend',) # 自定义登录from django.urls import path, re_path, include
from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView
urlpatterns = [
# ...
path('api-auth/', include('rest_framework.urls')), # 调试api的认证接口
path('api/schema/', SpectacularAPIView.as_view(), name='schema'), # OpenAPI 原始 schema (API数据源)
path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'), # Swagger UI (后端使用,注重调试)
path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'), # ReDoc (前端使用,注重美观)
]官方安装指引:官方文档
同步数据
uv sync
# 同步数据库(这一步需要完成自定义用户模型后再操作)
python manage.py makemigrations
python manage.py migrate
# 创建超级管理员
python manage.py createsuperuser
# 收集静态文件
python manage.py collectstaticdocker部署脚本
# 登录阿里云私有镜像仓库
echo $DOCKER_PASSWORD | docker login --username $DOCKER_USERNAME --password-stdin registry.cn-hangzhou.aliyuncs.com
# 先拉取新的镜像
docker pull $IMAGE
# 创建并启动容器
docker rm -f $CI_SOURCE_NAME 2>/dev/null || true
docker run -d --restart=unless-stopped --name=$CI_SOURCE_NAME --network host -p 19028:19028 $IMAGE
# 清理未使用的镜像
docker image prune -a -f跨域配置
这里一般在开发时配置跨域,正式环境可以在nginx配置跨域。也可以在前端配置代理来解决开发时的跨域问题
from django.utils.deprecation import MiddlewareMixin
class CorsMiddleware(MiddlewareMixin):
def process_response(self, request, response):
response["Access-Control-Allow-Credentials"] = "true"
response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Headers"] = "*"
response["Access-Control-Expose-Headers"] = "*"
response["Access-Control-Allow-Methods"] = "*"
# response["Access-Control-Max-Age"] = "*"
return response请在
settings.py中的MIDDLEWARE中添加utils.app.middleware.CorsMiddleware
manage.py其他命令
manage.py help # 帮助
python manage.py runserver 0.0.0.0:8000 # 启动开发服务器
python manage.py shell # 启动一个项目环境终端
python manage.py dbshell # 调用当前项目的models.py中的API
python manage.py makemigrations # (在自定义的App中的migrations包内)创建迁移文件
python manage.py migrate # 迁移数据库,生成表
python manage.py inspectdb # 从数据库反向生成数据模型
python manage.py flush # 清空数据库
python manage.py dumpdata appname > appname.json # 导出数据
python manage.py loaddata appname.json # 导入数据(表的关系比较复杂的时候可能导入不成功,最大的好处就是可以跨数据库进行导入导出)
python manage.py changepassword username # 修改用户密码
# 放在app下static中的静态文件全部拷贝到 settings.py 中设置的 STATIC_ROOT 文件夹中
python manage.py collectstatic # 部署时收集静态文件
django-admin.py makemessages -l zh-cn # 生成需要翻译的文件
django-admin.py compilemessages # 手工翻译 locale 中的文本后,我们需要编译一下,这样翻译才会生效sentry日志配置
sentry django配置 官方文档uv add sentry-sdk[django]
import sentry_sdk
# https://docs.sentry.io/platforms/python/integrations/django/
sentry_sdk.init(
dsn="https://a442b73dee2c3c429fbc07c260421f4e@o1342071.ingest.us.sentry.io/4108787128008704",
send_default_pii=True, # 是否发送用户隐私信息(IP、请求头等)
traces_sample_rate=1.0, # 性能监控采样率(0~1,1 为记录全部)
profiles_sample_rate=1.0, # 性能分析采样率(0~1,1 为记录全部)
)django Trunc功能数据库功能支持
需要给mysql添加时区信息,不然django的Trunc功能无法使用。
Trunc功能:from django.db.models.functions import TruncDate, Trunc
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
# 查询时区表信息是否存在
mysql -u root -p123456 -e "SELECT COUNT(*) FROM mysql.time_zone_name;"
mysql -u root -p123456 -e "SELECT * FROM mysql.time_zone_name WHERE Name='Asia/Shanghai';"