如何在Django的values()方法中获取模型的@property自定义属性值?
解决Django中@property字段无法在values()中使用的问题
嘿,我懂你碰到的问题了——你定义了created_date这个@property来格式化created_at日期,但直接在values()里调用它就报错。这是因为values()是直接和数据库交互的,它只能查询数据库里实际存在的字段,而created_date是Python层面生成的属性,数据库根本没有这个字段,所以Django找不到它。
下面给你几种可行的解决方案,你可以根据自己的需求挑:
方案一:在数据库层面用annotate格式化日期(推荐)
这种方法让数据库直接帮我们格式化日期,效率更高,还能和values()完美配合。不过得根据你用的数据库类型调整代码:
如果你用PostgreSQL
PostgreSQL支持TO_CHAR函数来格式化日期,我们可以用Django的Func来调用它:
from django.db.models import Func, Value, models # 在views.py中 data = Subs.objects.annotate( created_date=Func( 'created_at', Value('FMMonth DD YYYY'), # FM用来去掉月份、日期前的冗余空格 function='TO_CHAR', output_field=models.CharField() ) ).values('somevalues', 'created_date')
如果你用MySQL
MySQL用DATE_FORMAT函数,我们可以自定义一个Func类封装:
from django.db.models import Func, models class DateFormat(Func): function = 'DATE_FORMAT' template = "%(function)s(%(expressions)s, '%(format)s')" # 在views.py中 data = Subs.objects.annotate( created_date=DateFormat('created_at', format='%M %d %Y') ).values('somevalues', 'created_date')
方案二:先查基础字段,再在Python层面处理
如果不想依赖数据库函数,或者需要更复杂的格式化逻辑,可以先把created_at和其他需要的字段查出来,再在Python里转换:
# 在views.py中 # 先获取包含created_at的原始数据 raw_data = Subs.objects.values('somevalues', 'created_at') # 遍历转换created_date data = [] for item in raw_data: formatted_date = None if item['created_at']: # 如果created_at是datetime对象,直接调用strftime;如果是字符串就先转成datetime formatted_date = item['created_at'].strftime('%B %d %Y') data.append({ 'somevalues': item['somevalues'], 'created_date': formatted_date })
这种方法的好处是不依赖数据库类型,逻辑灵活,但数据量大的时候性能会不如数据库层面处理。
方案三:封装成自定义QuerySet/Manager(优雅之选)
如果你需要频繁用这个功能,可以把格式化逻辑封装到模型的QuerySet里,调用起来更简洁:
# 在models.py中 from django.db import models from django.db.models import Func, Value class SubsQuerySet(models.QuerySet): def with_created_date(self): # 这里根据你的数据库类型选择实现,比如PostgreSQL版本 return self.annotate( created_date=Func( 'created_at', Value('FMMonth DD YYYY'), function='TO_CHAR', output_field=models.CharField() ) ) class SubsManager(models.Manager): def get_queryset(self): return SubsQuerySet(self.model, using=self._db) class Subs(models.Model): # ... 你的其他字段 created_at = models.DateTimeField(auto_now_add=True, db_column="order_date", null=True, blank=True) objects = SubsManager() @property def created_date(self): return self.created_at.strftime('%B %d %Y') if self.created_at else None
然后在views.py里调用就超简单:
data = Subs.objects.with_created_date().values('somevalues', 'created_date')
注意点
别忘了你的created_at字段设置了null=True,所以无论用哪种方法,都要处理created_at为空的情况,避免出现AttributeError或者数据库报错。
内容的提问来源于stack exchange,提问作者Luff li
相关产品推荐
相关产品推荐

