Django __init__.py 高级应用速记

Django init.py 高级应用速记

一、标记 Python 包

markdown 复制代码
myapp/
  __init__.py    ← 有此文件,myapp 才是可导入的包
  models.py
  views.py

没有它,Python 不认识这个目录。Python 3.3+ 引入了隐式命名空间包,但显式 __init__.py 仍是主流实践,因为可以执行初始化代码。


二、控制导出范围(all)

当别人 from myapp import * 时,只导出 __all__ 中声明的符号:

python 复制代码
# myapp/__init__.py
__all__ = ['UserService', 'OrderService', '__version__']

from .services.user import UserService
from .services.order import OrderService

__version__ = '1.2.3'

def _internal_helper():  # 不在 __all__ 中,_ 前缀暗示私有
    pass

三、版本管理

python 复制代码
# myapp/__init__.py
__version__ = '1.2.3'
VERSION = (1, 2, 3)

setup.py 或 pyproject.toml 也可以直接引用:

python 复制代码
# setup.py
import myapp
setup(version=myapp.__version__)

四、美化 import 路径(门面模式)

python 复制代码
# myapp/__init__.py
from .services.auth import login, logout, get_current_user
from .services.order import create_order, cancel_order
from .models import User, Order
from .exceptions import AuthError, PermissionDenied

使用者只需一行:

python 复制代码
from myapp import login, create_order, User, AuthError

五、延迟导入(懒加载,解决循环引用)

python 复制代码
# myapp/__init__.py
__all__ = ['UserService', 'send_email']

def __getattr__(name):
    """Python 3.7+ 支持模块级 __getattr__"""
    if name == 'UserService':
        from .services.user import UserService
        return UserService
    if name == 'send_email':
        from .tasks.email import send_email
        return send_email
    raise AttributeError(f"module 'myapp' has no attribute '{name}'")

好处:首次访问时才导入,启动快,避免循环导入报错。


六、App 配置与信号注册

python 复制代码
# myapp/__init__.py
default_app_config = 'myapp.apps.MyAppConfig'
python 复制代码
# myapp/apps.py
from django.apps import AppConfig

class MyAppConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'myapp'
    verbose_name = '我的应用'

    def ready(self):
        # 注册信号
        import myapp.signals

        # 注册检查项
        from .checks import register_checks
        register_checks()

        # 初始化缓存
        from django.core.cache import cache
        cache.set('app_started', True)

关键提醒 :ready() 中导入模块放内部,避免在模块级导入时引起循环引用。


七、自定义系统检查(System Checks)

python 复制代码
# myapp/checks.py
from django.core.checks import register, Warning, Error

def check_redis_connection(app_configs, **kwargs):
    errors = []
    try:
        from django.core.cache import caches
        caches['default'].get('test')
    except Exception:
        errors.append(Error('Redis 连接失败', hint='检查 settings.CACHES', id='myapp.E001'))
    return errors

def register_checks():
    register(check_redis_connection)

运行 python manage.py check 即可验证------Django 自动扫描。


八、管理后台配置

python 复制代码
# myapp/admin.py
from django.contrib import admin
from .models import User, Order

@admin.register(User)
class UserAdmin(admin.ModelAdmin):
    list_display = ['id', 'username', 'email', 'is_active']
    search_fields = ['username', 'email']

admin.site.site_header = '我的管理后台'
admin.site.site_title = '管理后台'
admin.site.index_title = '功能导航'

九、注册全局模板标签与过滤器

python 复制代码
# myapp/templatetags/__init__.py  ← 必须存在
python 复制代码
# myapp/templatetags/my_filters.py
from django import template

register = template.Library()

@register.filter
def format_price(value):
    return f'¥{value:,.2f}'

@register.simple_tag
def current_time(format_string='%Y-%m-%d %H:%M'):
    from datetime import datetime
    return datetime.now().strftime(format_string)

模板中使用:

lua 复制代码
{% load my_filters %}
{{ order.price|format_price }}
{% current_time '%H:%M' %}

十、包级日志配置

python 复制代码
# myapp/__init__.py
import logging

logger = logging.getLogger(__name__)
logger.info(f'{__name__} 模块已加载')

配合 Django settings:

python 复制代码
LOGGING = {
    'loggers': {
        'myapp': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': False,
        },
    },
}

速查表

场景 关键文件 核心操作
标记包 __init__.py 文件存在即可
导出控制 __init__.py 定义 __all__
版本号 __init__.py 定义 __version__
懒加载 __init__.py 实现 __getattr__()
简化导入 __init__.py from .sub import X
信号注册 apps.py ready() 中 import signals
系统检查 checks.py @register 装饰器
管理后台 admin.py @admin.register 装饰器
模板标签 templatetags/ template.Library()
日志 __init__.py logging.getLogger(__name__)

核心思想 :__init__.py 是包的入口和初始化枢纽------标记身份、聚合导出、延迟加载、连接 Django 生命周期。


Django management/commands 自动注册速记

一、核心机制

Django 启动时会遍历 INSTALLED_APPS 中每个 app 的 management/commands/ 目录,该目录下所有非 _ 开头的 .py 文件 自动注册为 manage.py 子命令。

bash 复制代码
python manage.py my_command    ← 对应 myapp/management/commands/my_command.py

自动发现流程:

  1. Django 加载所有 INSTALLED_APPS
  2. 扫描每个 app 的 management/commands/ 目录
  3. 导入每个 .py 文件,找到 Command 类
  4. 文件名即命令名,加入 manage.py 的命令表

二、目录结构(铁律)

markdown 复制代码
myapp/
  management/
    __init__.py                    ← 必须空文件
    commands/
      __init__.py                  ← 必须空文件
      my_command.py                → python manage.py my_command
      sync_data.py                 → python manage.py sync_data
      _base.py                     ← _ 开头不注册(公共基类)

两个 __init__.py 缺一不可------少任何一个 Django 都找不到你的命令。


三、基本命令模板

python 复制代码
from django.core.management.base import BaseCommand

class Command(BaseCommand):
    help = '命令简短描述'

    def handle(self, *args, **options):
        self.stdout.write(self.style.SUCCESS('执行成功!'))

必须遵守 :类名固定为 Command,继承 BaseCommand,实现 handle()。


四、带参数的命令

python 复制代码
class Command(BaseCommand):
    help = '发送邮件给指定用户'

    def add_arguments(self, parser):
        # 位置参数
        parser.add_argument('email', type=str, help='邮箱地址')

        # 可选参数
        parser.add_argument('--subject', type=str, default='通知')

        # 布尔开关
        parser.add_argument('--dry-run', action='store_true')

        # 多值参数
        parser.add_argument('--cc', nargs='+', type=str)

    def handle(self, *args, **options):
        email = options['email']
        dry_run = options['dry_run']
        cc_list = options['cc'] or []

        if dry_run:
            self.stdout.write(f'[预览] 发送给 {email}, 抄送 {cc_list}')
        else:
            self.stdout.write(self.style.SUCCESS(f'已发送至 {email}'))

五、输出样式

python 复制代码
self.stdout.write(self.style.SUCCESS('绿色,成功'))
self.stdout.write(self.style.ERROR('红色,错误'))
self.stdout.write(self.style.WARNING('黄色,警告'))
self.stdout.write(self.style.NOTICE('蓝色,提示'))
self.stdout.write(self.style.MIGRATE_HEADING('加粗,用于迁移'))

六、事务控制

python 复制代码
from django.db import transaction

try:
    with transaction.atomic():
        User.objects.create(username='test')
        Order.objects.create(user=user, amount=100)
except Exception as e:
    self.stderr.write(self.style.ERROR(f'失败:{e}'))

七、进度条

python 复制代码
for i, user in enumerate(users, 1):
    self.stdout.write(f'处理中... {i}/{total}', ending='\r')
    self.stdout.flush()

self.stdout.write(self.style.SUCCESS(f'\n完成!共处理 {total} 条'))

八、提取公共基类

python 复制代码
# _base.py(_ 开头不注册为命令)
class BaseAppCommand(BaseCommand):
    def add_arguments(self, parser):
        parser.add_argument('--env', default='prod', choices=['dev', 'test', 'prod'])

    def handle(self, *args, **options):
        self.env = options['env']
        self._run(*args, **options)

    def _run(self, *args, **options):
        raise NotImplementedError('子类必须实现 _run')

子命令:

python 复制代码
from ._base import BaseAppCommand

class Command(BaseAppCommand):
    help = '同步数据'
    def _run(self, *args, **options):
        self.stdout.write(f'在 {self.env} 环境同步...')

九、子命令(子解析器)

python 复制代码
class Command(BaseCommand):
    help = '用户管理工具'

    def add_arguments(self, parser):
        subparsers = parser.add_subparsers(dest='action', required=True)

        create_parser = subparsers.add_parser('create', help='创建用户')
        create_parser.add_argument('username', type=str)

        delete_parser = subparsers.add_parser('delete', help='删除用户')
        delete_parser.add_argument('username', type=str)

        subparsers.add_parser('list', help='列出用户')

    def handle(self, *args, **options):
        action = options['action']
        if action == 'create':
            self._create_user(options['username'])
        elif action == 'delete':
            self._delete_user(options['username'])
        elif action == 'list':
            self._list_users()

用法:

bash 复制代码
python manage.py user create alice
python manage.py user delete bob
python manage.py user list

十、执行系统命令

python 复制代码
import subprocess

result = subprocess.run(['ls', '-la'], capture_output=True, text=True, timeout=10)
if result.returncode == 0:
    self.stdout.write(result.stdout)

十一、确认交互

python 复制代码
def handle(self, *args, **options):
    answer = input('确认执行?输入 yes 继续: ')
    if answer.lower() != 'yes':
        self.stdout.write(self.style.WARNING('已取消'))
        return
    # 执行操作

速查表

需求 关键代码
基础命令 class Command(BaseCommand): handle()
参数 parser.add_argument(...)
彩色输出 self.style.SUCCESS(msg)
进度条 ending='\r' + self.stdout.flush()
公用基类 下划线开头,放 handle 模板逻辑
子命令 parser.add_subparsers(dest='action')
事务 with transaction.atomic():
调用 shell subprocess.run([...])

核心思想 :文件名即命令名,目录放对即自动注册,Command 类实现 handle() 即开箱可用。


Dockerfile 速记

一、核心指令速查

指令 用途 关键点
FROM 指定基础镜像 每个 Dockerfile 第一条有效指令
WORKDIR 设置工作目录 不存在则自动创建
COPY 复制文件到镜像 比 ADD 更明确,仅复制文件
ADD 复制 + 自动解压 仅需要解压 tar 时用,其他用 COPY
RUN 构建时执行命令 每条生成一层,用 && 合并减少层数
ENV 设置环境变量 运行时也生效
ARG 构建参数 仅构建时可用,--build-arg 传入
EXPOSE 声明端口 仅声明,-p 才实际映射
CMD 默认启动命令 可被 docker run 的参数覆盖
ENTRYPOINT 固定入口点 CMD 的内容成为它的默认参数
VOLUME 声明匿名卷 数据持久化
USER 切换运行用户 不用 root 运行应用
HEALTHCHECK 健康检查 判断容器是否真正就绪
LABEL 添加元数据 LABEL maintainer="xxx"

二、CMD vs ENTRYPOINT

CMD(可覆盖)

dockerfile 复制代码
CMD ["python", "app.py"]
# docker run myimage                   → python app.py
# docker run myimage python test.py    → python test.py(覆盖 CMD)

ENTRYPOINT(不可覆盖)

dockerfile 复制代码
ENTRYPOINT ["python"]
CMD ["app.py"]
# docker run myimage                   → python app.py
# docker run myimage test.py           → python test.py(CMD 被替换)

组合(推荐)

dockerfile 复制代码
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]
# 固定入口脚本 + 可替换的默认参数

三、Django 项目标准 Dockerfile

dockerfile 复制代码
# ---- 构建阶段 ----
FROM python:3.11-slim AS builder

WORKDIR /app

# 安装系统依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
    gcc libpq-dev \
    && rm -rf /var/lib/apt/lists/*

# 安装 Python 依赖
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

# ---- 运行阶段 ----
FROM python:3.11-slim

WORKDIR /app

COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH

COPY . .

RUN useradd -m -u 1000 django && chown -R django:django /app
USER django

EXPOSE 8000

CMD ["gunicorn", "myproject.wsgi:application", "-b", "0.0.0.0:8000"]

四、多阶段构建

dockerfile 复制代码
# 阶段1:编译
FROM golang:1.21 AS builder
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o /app .

# 阶段2:运行(精简镜像)
FROM alpine:3.19
COPY --from=builder /app /app
ENTRYPOINT ["/app"]

好处:最终镜像只含运行时二进制,体积从 800MB 降到 10MB。


五、层缓存优化

❌ 差(每次代码变动都重新安装依赖)

dockerfile 复制代码
COPY . .
RUN pip install -r requirements.txt

✅ 好(依赖层缓存,代码变动不影响)

dockerfile 复制代码
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

原则:变动频率低的放前面,变动频率高的放后面。

sql 复制代码
FROM           ← 几乎不变
RUN apt-get    ← 很少变
COPY req.txt   ← 偶尔变
RUN pip install← 偶尔变
COPY . .        ← 经常变

六、.dockerignore

markdown 复制代码
__pycache__
*.pyc
*.pyo
.pytest_cache
.git
.gitignore
.env
venv/
.venv/
node_modules/
*.md
Dockerfile
docker-compose.yml
*.log

七、HEALTHCHECK

dockerfile 复制代码
# Django
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD curl -f http://localhost:8000/health/ || exit 1

# PostgreSQL
HEALTHCHECK --interval=30s --timeout=3s \
  CMD pg_isready -U postgres || exit 1

配合 docker-compose:

yaml 复制代码
services:
  web:
    depends_on:
      db:
        condition: service_healthy

八、VOLUME 持久化

dockerfile 复制代码
VOLUME ["/var/lib/postgresql/data"]
bash 复制代码
docker run -v my_volume:/var/lib/postgresql/data myimage  # 命名卷
docker run -v /host/path:/container/path myimage            # 绑定挂载
方式 命令 适用场景
命名卷 -v vol:/path 生产环境
绑定挂载 -v /host:/container 开发环境
tmpfs --tmpfs /path 临时敏感数据

九、环境变量与配置

dockerfile 复制代码
# 构建时
ARG APP_VERSION=1.0
RUN echo "Building version $APP_VERSION"

# 运行时
ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    DJANGO_SETTINGS_MODULE=myproject.settings.production

# 运行时传入
# docker run -e DATABASE_URL=xxx myimage

十、USER 安全

dockerfile 复制代码
RUN groupadd -r appgroup && useradd -r -g appgroup -u 1000 appuser
COPY --chown=appuser:appgroup . /app
USER appuser
# 此时所有 RUN、CMD、ENTRYPOINT 都以 appuser 身份运行

十一、构建与推送

bash 复制代码
# 构建
docker build -t username/myapp:1.0 .

# 带构建参数
docker build --build-arg APP_VERSION=2.0 -t myapp:2.0 .

# 推送到仓库
docker push username/myapp:1.0

# 多平台构建+推送
docker buildx build --platform linux/amd64,linux/arm64 \
  -t username/myapp:1.0 --push .

十二、Entrypoint 初始化脚本

dockerfile 复制代码
COPY docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod +x /docker-entrypoint.sh
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["gunicorn", "app.wsgi"]
bash 复制代码
#!/bin/bash
set -e

# 等待数据库就绪
if [ "$DATABASE_URL" ]; then
  echo "等待数据库..."
  while ! nc -z db 5432; do sleep 1; done
fi

# 执行迁移
python manage.py migrate --noinput

# 执行 CMD
exec "$@"

速查表

场景 指令/模式 关键点
安装依赖 apt-get ... && rm -rf /var/lib/apt/lists/* 清理缓存减小体积
Python 依赖 pip install --no-cache-dir 先 COPY req.txt 用层缓存
多阶段构建 FROM ... AS builder + COPY --from=builder 最终镜像只含运行时
默认命令 CMD ["executable", "param1"] JSON 数组形式
初始化 entrypoint 脚本 exec "$@" 保证信号传递
数据持久化 VOLUME + -v 挂载 容器重启数据不丢
缩小体积 多阶段 + slim/alpine + .dockerignore 能省则省
安全 USER + COPY --chown 不用 root 运行

核心思想:层缓存、多阶段构建、安全最小化 ------ 构建快、镜像小、运行稳。

相关推荐
软件工程师文艺6661 小时前
Teamcenter FMS 文件管理系统深度解析:缓存策略、SSL 部署与性能调优
后端
Gopher_HBo1 小时前
类型系统 —— Type、rtype 与运行时类型元数据
后端
今天早睡1 小时前
并发分支、同步汇总与 State 合并
后端·架构
王川7081 小时前
商协会后台前端的模块聚合实践
前端·后端
旋律4141 小时前
String、StringBuilder、StringBuffer:三个"字符串",到底区别在哪?
java·后端
小卿噢1 小时前
94% 的集群在空等——数据倾斜实测
后端
没事写点啥1 小时前
WeKnora 问答实现
后端·agent·ai编程
五百万啊啊啊啊1 小时前
RSA+AES 混合加密的三个坑
后端
糖果豆豆1 小时前
监控全绿,线上还是抖了一下——加埋点只能解决三种"看不见"里的一种
后端·架构