如何在Django及DRF中实现PUT/update动作时自动更新updated_time字段?
问题1:查找Django models.py中的HTTP动作
首先得澄清一下:Django的models.py是用来定义数据库模型的,本身并不直接包含HTTP动作。HTTP动作(比如GET、POST、PUT、DELETE这类)是在视图(Views)、视图集(ViewSets)或者路由配置里处理的,这些组件会把HTTP请求和模型的操作(比如创建、更新、删除实例)关联起来。
如果你想找到和某个模型关联的HTTP动作,通常可以这么做:
- 查看项目的
views.py(或者DRF的ViewSet文件):比如DRF的ModelViewSet默认提供了和模型对应的CRUD动作,对应关系是:list→ GET(获取模型实例列表)retrieve→ GET(获取单个模型实例)create→ POST(创建新实例)update→ PUT(全量更新实例)partial_update→ PATCH(部分更新实例)destroy→ DELETE(删除实例)
- 查看
urls.py:路由会把特定的URL路径映射到对应的视图/视图集动作,从中你能看到哪些HTTP方法被绑定到了模型操作上。
举个例子,如果你的Pet模型用了ModelViewSet,视图代码可能是这样的:
from rest_framework import viewsets from .models import Pet from .serializers import PetSerializer class PetViewSet(viewsets.ModelViewSet): queryset = Pet.objects.all() serializer_class = PetSerializer
这个ViewSet默认就绑定了所有CRUD对应的HTTP动作,你可以在路由里用router.register(r'pets', PetViewSet)来注册,这样就能通过对应的HTTP方法操作Pet模型了。
问题2:PUT/update动作时自动更新updated_time字段
针对你的Pet模型,要实现当执行PUT请求(对应DRF的update动作)时自动更新updated_time,有几种简洁的实现方式,推荐两种最常用的:
方法1:重写DRF序列化器的update方法
这是最直接的方式,因为序列化器负责处理请求数据和模型实例的交互:
from rest_framework import serializers from .models import Pet from datetime import datetime class PetSerializer(serializers.ModelSerializer): class Meta: model = Pet fields = ['kind', 'updated_time'] def update(self, instance, validated_data): # 不管请求里有没有传updated_time,都强制更新为当前时间 instance.updated_time = datetime.now() # 处理其他字段的更新 instance.kind = validated_data.get('kind', instance.kind) instance.save() return instance
这样每次调用序列化器的update方法(也就是处理PUT/PATCH请求时),都会自动把updated_time设为当前时间。
方法2:使用Django信号
如果希望在模型实例被更新时(不管是通过API还是后台管理)都自动更新时间,信号是更通用的方案:
from django.db.models.signals import pre_save from django.dispatch import receiver from .models import Pet from datetime import datetime @receiver(pre_save, sender=Pet) def update_updated_time(sender, instance, **kwargs): # 只在实例已经存在(即更新操作,不是创建)时更新时间 if instance.pk is not None: instance.updated_time = datetime.now()
然后记得在apps.py里注册信号(或者在models.py末尾加pre_save.connect(update_updated_time, sender=Pet)),这样每次更新Pet实例(包括API的PUT动作)都会触发这个信号,自动更新时间。
额外优化:用auto_now字段参数?
哦对了,Django模型字段有个auto_now参数,如果你希望**每次保存实例(包括创建和更新)**都自动更新时间,可以直接这么定义字段:
updated_time = models.DateTimeField(auto_now=True)
但如果你的需求是只在更新(PUT/update动作)时更新,创建时不自动设置,那上面两种方法更合适,因为auto_now=True会在创建实例时也设置时间。
内容的提问来源于stack exchange,提问作者Wave1988

