Python中Django ORM如何与attrs库一起使用?

我在 model class 上直接使用 attr.s 修饰符,server 直接 down 了,还没查到具体原因,不过大佬有使用过它们的组合呢?


Python中Django ORM如何与attrs库一起使用?
1 回复

在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。

回到顶部