Python中Django ORM如何与attrs库一起使用?
我在 model class 上直接使用 attr.s 修饰符,server 直接 down 了,还没查到具体原因,不过大佬有使用过它们的组合呢?
Python中Django ORM如何与attrs库一起使用?
在Django项目里,attrs库和ORM一起用主要是为了在业务逻辑层创建清晰的数据类,替代臃肿的models.py或到处散落的字典。核心思路是:用attrs定义纯粹的数据结构,用ORM处理持久化,两者通过显式转换来协作。
一个典型的场景是处理复杂的表单数据或API请求。比如你有个用户注册流程,前端传过来的数据比User模型字段多,或者需要一些临时计算字段。这时可以用attrs定义一个UserRegistrationData类,它包含邮箱、密码、确认密码、邀请码等字段。在这个类里你可以用attrs的验证器(@attrs.define配合field(validator=...))做轻量级验证,比如检查密码强度、确认密码是否匹配。
验证和处理完业务逻辑后,再手动提取需要的数据创建Django模型实例。反过来,从数据库取出模型实例后,也可以根据需要转换成attrs对象,方便在业务层传递。
下面是个具体例子。假设我们有个简单的博客应用,要处理文章创建:
# 首先,定义attrs数据类
import attrs
from attrs import field, validators
from django.utils import timezone
from typing import Optional
@attrs.define
class ArticleCreateData:
title: str = field(validator=validators.min_len(5))
content: str = field(validator=validators.min_len(10))
author_id: int = field(validator=validators.instance_of(int))
is_published: bool = field(default=False)
scheduled_publish_time: Optional[timezone.datetime] = field(default=None)
# 可以添加自定义业务逻辑方法
def should_schedule(self) -> bool:
return self.scheduled_publish_time is not None and self.scheduled_publish_time > timezone.now()
# 然后,在视图或服务层中使用
from django.http import JsonResponse
from .models import Article, User
from django.core.exceptions import ValidationError
def create_article_view(request):
try:
# 1. 从请求中获取数据,创建attrs对象
data = ArticleCreateData(
title=request.POST.get('title'),
content=request.POST.get('content'),
author_id=int(request.POST.get('author_id')),
is_published=request.POST.get('is_published', 'false') == 'true'
)
# 2. attrs会自动运行字段验证器
# 3. 执行自定义业务逻辑
if data.should_schedule():
# 处理定时发布逻辑
pass
# 4. 转换为Django模型
try:
author = User.objects.get(id=data.author_id)
except User.DoesNotExist:
return JsonResponse({'error': 'Author not found'}, status=400)
article = Article(
title=data.title,
content=data.content,
author=author,
is_published=data.is_published,
published_at=data.scheduled_publish_time if data.is_published else None
)
# 5. 保存到数据库
article.save()
return JsonResponse({'id': article.id, 'title': article.title})
except (ValueError, attrs.exceptions.NotAnAttrsClassError,
attrs.exceptions.AttributeValidationError) as e:
# 处理attrs验证错误
return JsonResponse({'error': str(e)}, status=400)
except ValidationError as e:
# 处理Django模型验证错误
return JsonResponse({'error': dict(e)}, status=400)
# 反向转换的例子:从ORM实例创建attrs对象
def get_article_data(article_id):
article = Article.objects.select_related('author').get(id=article_id)
# 转换为attrs对象,可能用于序列化或业务处理
article_data = ArticleCreateData(
title=article.title,
content=article.content,
author_id=article.author.id,
is_published=article.is_published
)
return article_data
这样做的几个好处:1) 关注点分离,attrs类只管数据结构和基础验证,模型只管持久化;2) 类型提示更友好,配合mypy或pyright能做静态检查;3) 测试更方便,可以单独测试业务逻辑而不依赖数据库。
不过要注意,这不是Django社区的主流做法,大部分情况下直接用ModelForm或DRM Serializer更直接。只有在业务逻辑特别复杂、需要严格区分数据层和持久层时,才值得引入这个额外抽象。
总结:用attrs定义业务数据对象,手动桥接到Django ORM。

