django-dev
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDjango Development Patterns
Django开发范式
Opinionated Django development toolkit enforcing consistent, production-ready patterns.
约定式Django开发工具包,强制执行一致的、可用于生产环境的开发模式。
Core Principles
核心原则
- One file = one model/form - Each model and form lives in its own file
- Consistent prefixes - Abstract (), virtual (
Base*), proxy (Virtual*)Proxy* - UUID primary keys - All models use UUID instead of auto-increment
- Timestamps everywhere - All models inherit created_at/updated_at
- Soft delete by default - Use deleted_at instead of hard deletes
- Dynaconf for config - Never use plain settings.py
- uv + pyproject.toml - Use uv for package management with split deps
- Class member ordering - Strict ordering for readability
- Docker in /docker - All Docker artifacts in folder
/docker
- 一个文件对应一个模型/表单 - 每个模型和表单单独存放在一个文件中
- 统一前缀规则 - 抽象类()、虚拟类(
Base*)、代理类(Virtual*)Proxy* - UUID主键 - 所有模型使用UUID而非自增ID
- 全局时间戳 - 所有模型均继承created_at/updated_at字段
- 默认软删除 - 使用deleted_at字段而非硬删除
- 用Dynaconf管理配置 - 绝不使用原生settings.py
- uv + pyproject.toml - 使用uv进行包管理,拆分依赖项
- 类成员排序 - 严格的排序规则以提升可读性
- Docker文件存放在/docker目录 - 所有Docker相关文件均放在文件夹中
/docker
Project Setup (uv + pyproject.toml)
项目搭建(uv + pyproject.toml)
Always use uv for package management with split dependencies:
bash
undefined始终使用uv进行包管理,并拆分依赖项:
bash
undefinedInitialize project
Initialize project
uv init myproject
cd myproject
uv init myproject
cd myproject
Add dependencies by group
Add dependencies by group
uv add django dynaconf django-unfold django-ninja
uv add --group dev ruff mypy django-stubs
uv add --group test pytest pytest-django factory-boy pytest-cov
`pyproject.toml`:
```toml
[project]
name = "myproject"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"django>=5.0",
"dynaconf[toml]>=3.2",
"django-unfold>=0.30",
"django-ninja>=1.0",
"psycopg[binary]>=3.1",
"whitenoise>=6.6",
]
[dependency-groups]
dev = [
"ruff>=0.3",
"mypy>=1.8",
"django-stubs>=4.2",
"ipython>=8.0",
]
test = [
"pytest>=8.0",
"pytest-django>=4.8",
"factory-boy>=3.3",
"pytest-cov>=4.1",
"freezegun>=1.4",
]
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.mypy]
plugins = ["mypy_django_plugin.main"]
strict = trueuv add django dynaconf django-unfold django-ninja
uv add --group dev ruff mypy django-stubs
uv add --group test pytest pytest-django factory-boy pytest-cov
`pyproject.toml`:
```toml
[project]
name = "myproject"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"django>=5.0",
"dynaconf[toml]>=3.2",
"django-unfold>=0.30",
"django-ninja>=1.0",
"psycopg[binary]>=3.1",
"whitenoise>=6.6",
]
[dependency-groups]
dev = [
"ruff>=0.3",
"mypy>=1.8",
"django-stubs>=4.2",
"ipython>=8.0",
]
test = [
"pytest>=8.0",
"pytest-django>=4.8",
"factory-boy>=3.3",
"pytest-cov>=4.1",
"freezegun>=1.4",
]
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.mypy]
plugins = ["mypy_django_plugin.main"]
strict = trueProject Structure
项目结构
Standard Django project layout:
project/
├── pyproject.toml # Dependencies (uv)
├── uv.lock # Lock file
├── docker/
│ ├── Dockerfile # Main Dockerfile
│ ├── Dockerfile.dev # Development Dockerfile
│ ├── docker-compose.yml # Main compose
│ ├── docker-compose.dev.yml
│ ├── nginx/
│ │ └── nginx.conf
│ └── scripts/
│ ├── entrypoint.sh
│ └── wait-for-it.sh
├── config/
│ ├── __init__.py
│ ├── settings.py # Dynaconf integration
│ ├── .secrets.toml # Gitignored secrets
│ └── settings.toml # Environment config
├── apps/
│ └── myapp/
│ ├── models/ # Package, not single file
│ │ ├── __init__.py
│ │ ├── base.py # Base classes
│ │ └── user.py # One model per file
│ ├── forms/
│ │ ├── __init__.py
│ │ └── user.py
│ ├── managers/
│ │ ├── __init__.py
│ │ └── user.py
│ ├── api/ # Django Ninja (see django-dev-ninja)
│ └── admin/ # Unfold admin (see django-dev-unfold)
├── tests/ # See django-dev-test
└── manage.py标准Django项目布局:
project/
├── pyproject.toml # Dependencies (uv)
├── uv.lock # Lock file
├── docker/
│ ├── Dockerfile # Main Dockerfile
│ ├── Dockerfile.dev # Development Dockerfile
│ ├── docker-compose.yml # Main compose
│ ├── docker-compose.dev.yml
│ ├── nginx/
│ │ └── nginx.conf
│ └── scripts/
│ ├── entrypoint.sh
│ └── wait-for-it.sh
├── config/
│ ├── __init__.py
│ ├── settings.py # Dynaconf integration
│ ├── .secrets.toml # Gitignored secrets
│ └── settings.toml # Environment config
├── apps/
│ └── myapp/
│ ├── models/ # Package, not single file
│ │ ├── __init__.py
│ │ ├── base.py # Base classes
│ │ └── user.py # One model per file
│ ├── forms/
│ │ ├── __init__.py
│ │ └── user.py
│ ├── managers/
│ │ ├── __init__.py
│ │ └── user.py
│ ├── api/ # Django Ninja (see django-dev-ninja)
│ └── admin/ # Unfold admin (see django-dev-unfold)
├── tests/ # See django-dev-test
└── manage.pyDocker Configuration
Docker配置
All Docker artifacts in folder:
/dockerdockerfile
undefined所有Docker相关文件均放在目录中:
/dockerdockerfile
undefineddocker/Dockerfile
docker/Dockerfile
FROM python:3.12-slim
WORKDIR /app
FROM python:3.12-slim
WORKDIR /app
Install uv
Install uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
Install dependencies
Install dependencies
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev
Copy application
Copy application
COPY . .
COPY . .
Collect static files
Collect static files
RUN uv run python manage.py collectstatic --noinput
EXPOSE 8000
CMD ["uv", "run", "gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000"]
```yamlRUN uv run python manage.py collectstatic --noinput
EXPOSE 8000
CMD ["uv", "run", "gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000"]
```yamldocker/docker-compose.yml
docker/docker-compose.yml
services:
web:
build:
context: ..
dockerfile: docker/Dockerfile
ports:
- "8000:8000"
environment:
- DJANGO_ENV=production
env_file:
- ../.env
depends_on:
- db
db:
image: postgres:16-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=myproject
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
postgres_data:
undefinedservices:
web:
build:
context: ..
dockerfile: docker/Dockerfile
ports:
- "8000:8000"
environment:
- DJANGO_ENV=production
env_file:
- ../.env
depends_on:
- db
db:
image: postgres:16-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=myproject
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
postgres_data:
undefinedModel Organization
模型组织
Base Classes
基类
Create base classes in :
models/base.pypython
import uuid
from django.db import models
from django.utils import timezone
class BaseTimeStamped(models.Model):
"""Adds created_at and updated_at timestamps."""
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
get_latest_by = "created_at"
class BaseSoftDelete(models.Model):
"""Adds soft delete capability with deleted_at field."""
deleted_at = models.DateTimeField(null=True, blank=True, db_index=True)
class Meta:
abstract = True
def delete(self, using=None, keep_parents=False):
self.deleted_at = timezone.now()
self.save(update_fields=["deleted_at"])
def hard_delete(self):
super().delete()
@property
def is_deleted(self) -> bool:
return self.deleted_at is not None
class BaseUUID(models.Model):
"""Uses UUID as primary key instead of auto-increment."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
class Meta:
abstract = True
class BaseModel(BaseUUID, BaseTimeStamped, BaseSoftDelete):
"""Standard base model with UUID, timestamps, and soft delete."""
class Meta:
abstract = True在中创建基类:
models/base.pypython
import uuid
from django.db import models
from django.utils import timezone
class BaseTimeStamped(models.Model):
"""Adds created_at and updated_at timestamps."""
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
get_latest_by = "created_at"
class BaseSoftDelete(models.Model):
"""Adds soft delete capability with deleted_at field."""
deleted_at = models.DateTimeField(null=True, blank=True, db_index=True)
class Meta:
abstract = True
def delete(self, using=None, keep_parents=False):
self.deleted_at = timezone.now()
self.save(update_fields=["deleted_at"])
def hard_delete(self):
super().delete()
@property
def is_deleted(self) -> bool:
return self.deleted_at is not None
class BaseUUID(models.Model):
"""Uses UUID as primary key instead of auto-increment."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
class Meta:
abstract = True
class BaseModel(BaseUUID, BaseTimeStamped, BaseSoftDelete):
"""Standard base model with UUID, timestamps, and soft delete."""
class Meta:
abstract = TrueNaming Conventions
命名规范
| Prefix | Type | Example |
|---|---|---|
| Abstract base class | |
| In-memory only (not persisted) | |
| Proxy model | |
| (none) | Regular model | |
| 前缀 | 类型 | 示例 |
|---|---|---|
| 抽象基类 | |
| 仅内存存在(不持久化) | |
| 代理模型 | |
| (无) | 常规模型 | |
Class Member Ordering
类成员排序规则
All classes follow strict member ordering:
- - ALWAYS FIRST in the class
class Meta - Fields - Class attributes (model fields)
- Managers -
objects = Manager() - Properties () - Alphabetical order
@property - Private/dunder methods (,
_method) - Alphabetical order__str__ - Public methods - Alphabetical order
python
class User(BaseModel):
"""User account model."""
# 1. class Meta - ALWAYS FIRST
class Meta:
db_table = "users"
ordering = ["-created_at"]
# 2. Fields
email = models.EmailField(unique=True)
name = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
# 3. Manager
objects = UserManager()
# 4. Properties (alphabetical)
@property
def display_name(self) -> str:
return self.name or self.email.split("@")[0]
@property
def is_verified(self) -> bool:
return self.email_verified_at is not None
# 5. Private/dunder methods (alphabetical)
def __repr__(self) -> str:
return f"<User {self.email}>"
def __str__(self) -> str:
return self.email
def _calculate_score(self) -> int:
return len(self.orders.all())
def _validate_status(self) -> bool:
return self.is_active
# 6. Public methods (alphabetical)
def activate(self) -> None:
self.is_active = True
self.save(update_fields=["is_active"])
def can_place_order(self) -> bool:
return self.is_active and not self.is_deleted
def deactivate(self) -> None:
self.is_active = False
self.save(update_fields=["is_active"])所有类均遵循严格的成员排序:
- - 必须放在类的最开头
class Meta - 字段 - 类属性(模型字段)
- 管理器 -
objects = Manager() - 属性() - 按字母顺序排列
@property - 私有/双下划线方法(,
_method) - 按字母顺序排列__str__ - 公共方法 - 按字母顺序排列
python
class User(BaseModel):
"""User account model."""
# 1. class Meta - ALWAYS FIRST
class Meta:
db_table = "users"
ordering = ["-created_at"]
# 2. Fields
email = models.EmailField(unique=True)
name = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
# 3. Manager
objects = UserManager()
# 4. Properties (alphabetical)
@property
def display_name(self) -> str:
return self.name or self.email.split("@")[0]
@property
def is_verified(self) -> bool:
return self.email_verified_at is not None
# 5. Private/dunder methods (alphabetical)
def __repr__(self) -> str:
return f"<User {self.email}>"
def __str__(self) -> str:
return self.email
def _calculate_score(self) -> int:
return len(self.orders.all())
def _validate_status(self) -> bool:
return self.is_active
# 6. Public methods (alphabetical)
def activate(self) -> None:
self.is_active = True
self.save(update_fields=["is_active"])
def can_place_order(self) -> bool:
return self.is_active and not self.is_deleted
def deactivate(self) -> None:
self.is_active = False
self.save(update_fields=["is_active"])Model File Template
模型文件模板
Each model in its own file ():
models/user.pypython
from django.db import models
from .base import BaseModel
from ..managers.user import UserManager
class User(BaseModel):
"""User account model."""
# 1. class Meta - ALWAYS FIRST
class Meta:
db_table = "users"
verbose_name = "User"
verbose_name_plural = "Users"
ordering = ["-created_at"]
# 2. Fields
email = models.EmailField(unique=True)
name = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
# 3. Manager
objects = UserManager()
# 4. Properties
@property
def display_name(self) -> str:
return self.name or self.email.split("@")[0]
# 5. Private/dunder methods
def __str__(self) -> str:
return self.email每个模型单独存放在一个文件中():
models/user.pypython
from django.db import models
from .base import BaseModel
from ..managers.user import UserManager
class User(BaseModel):
"""User account model."""
# 1. class Meta - ALWAYS FIRST
class Meta:
db_table = "users"
verbose_name = "User"
verbose_name_plural = "Users"
ordering = ["-created_at"]
# 2. Fields
email = models.EmailField(unique=True)
name = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
# 3. Manager
objects = UserManager()
# 4. Properties
@property
def display_name(self) -> str:
return self.name or self.email.split("@")[0]
# 5. Private/dunder methods
def __str__(self) -> str:
return self.emailModel Package Init
模型包的__init__.py文件
Re-export all models in :
models/__init__.pypython
from .base import BaseModel, BaseTimeStamped, BaseSoftDelete, BaseUUID
from .user import User
from .product import Product
__all__ = [
"BaseModel",
"BaseTimeStamped",
"BaseSoftDelete",
"BaseUUID",
"User",
"Product",
]在中重新导出所有模型:
models/__init__.pypython
from .base import BaseModel, BaseTimeStamped, BaseSoftDelete, BaseUUID
from .user import User
from .product import Product
__all__ = [
"BaseModel",
"BaseTimeStamped",
"BaseSoftDelete",
"BaseUUID",
"User",
"Product",
]Custom Managers
自定义管理器
Place managers in package:
managers/python
undefined将管理器放在包中:
managers/python
undefinedmanagers/user.py
managers/user.py
from django.db import models
class UserQuerySet(models.QuerySet):
def active(self):
return self.filter(is_active=True, deleted_at__isnull=True)
def by_email(self, email: str):
return self.filter(email__iexact=email)class UserManager(models.Manager):
def get_queryset(self) -> UserQuerySet:
return UserQuerySet(self.model, using=self._db)
def active(self):
return self.get_queryset().active()
def by_email(self, email: str):
return self.get_queryset().by_email(email)undefinedfrom django.db import models
class UserQuerySet(models.QuerySet):
def active(self):
return self.filter(is_active=True, deleted_at__isnull=True)
def by_email(self, email: str):
return self.filter(email__iexact=email)class UserManager(models.Manager):
def get_queryset(self) -> UserQuerySet:
return UserQuerySet(self.model, using=self._db)
def active(self):
return self.get_queryset().active()
def by_email(self, email: str):
return self.get_queryset().by_email(email)undefinedDynaconf Configuration
Dynaconf配置
Always use Dynaconf for Django settings. See for complete setup.
references/dynaconf.mdQuick setup:
bash
pip install dynaconf
dynaconf init -f tomlUpdate :
config/settings.pypython
from dynaconf import Dynaconf
settings = Dynaconf(
envvar_prefix="DJANGO",
settings_files=["settings.toml", ".secrets.toml"],
environments=True,
env_switcher="DJANGO_ENV",
)始终使用Dynaconf管理Django设置。完整搭建指南请参考。
references/dynaconf.md快速搭建步骤:
bash
pip install dynaconf
dynaconf init -f toml更新:
config/settings.pypython
from dynaconf import Dynaconf
settings = Dynaconf(
envvar_prefix="DJANGO",
settings_files=["settings.toml", ".secrets.toml"],
environments=True,
env_switcher="DJANGO_ENV",
)Form Organization
表单组织
Forms follow the same 1-file-per-form pattern. See for details.
references/forms.mdpython
undefined表单遵循与模型相同的“一个文件对应一个表单”的模式。详细内容请参考。
references/forms.mdpython
undefinedforms/user.py
forms/user.py
from django import forms
from ..models import User
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ["email", "name"]
undefinedfrom django import forms
from ..models import User
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ["email", "name"]
undefinedCreating a New App
创建新应用
To create a new Django app with proper structure:
- Create app directory with packages:
bash
mkdir -p apps/myapp/{models,forms,managers,api,admin}
touch apps/myapp/__init__.py
touch apps/myapp/{models,forms,managers,api,admin}/__init__.py- Create base classes in
models/base.py - Add app to using Dynaconf
INSTALLED_APPS - Create initial models following conventions
按照规范结构创建新的Django应用:
- 创建应用目录及子包:
bash
mkdir -p apps/myapp/{models,forms,managers,api,admin}
touch apps/myapp/__init__.py
touch apps/myapp/{models,forms,managers,api,admin}/__init__.py- 在中创建基类
models/base.py - 通过Dynaconf将应用添加到中
INSTALLED_APPS - 遵循规范创建初始模型
Additional Resources
额外资源
Reference Files
参考文档
For detailed patterns and setup guides:
- - Advanced model patterns, relationships, constraints
references/models.md - - Form organization, validation, widgets
references/forms.md - - Complete Dynaconf setup and environment configuration
references/dynaconf.md
如需详细的模式和搭建指南:
- - 高级模型模式、关联关系、约束
references/models.md - - 表单组织、验证、小部件
references/forms.md - - 完整的Dynaconf搭建和环境配置
references/dynaconf.md
Related Skills
相关技能
- django-dev-ninja - API development with Django Ninja
- django-dev-unfold - Admin customization with Unfold
- django-dev-test - Testing with pytest and factories
- django-dev-ninja - 使用Django Ninja进行API开发
- django-dev-unfold - 使用Unfold自定义Admin后台
- django-dev-test - 使用pytest和工厂进行测试