如何解决如何在 django DRF 中处理时区而不重复太多?
-
简介:我的项目
TIME_ZONE
等于'UTC'
,而我的用户来自太多时区。因此,当我使用POST
或PUT
或date
字段创建time
或dateTime
时,我在 {{1} 之前将这些字段转换为UTC
}}。然后,当用户发出serializer.save()
请求时,我将相同的字段转换回用户的时区,即GET
request.user.timezone
# simplified functions
def localize(usertimzone,date,type):
"""
type = 'date','time','datetime'
"""
from dateutil import parser
date = parser.parse(date)
if not date.tzinfo:
usertimzone = pytz.timezone(usertimzone)
date = usertimzone.localize(date)
utc_date = date.astimezone(pytz.utc)
return utc_date
def normalize(usertimzone,type):
current_user_tz = pytz.timezone(usertimzone)
date = current_user_tz.localize(date)
return date
- 问题:我在太多视图中使用了这些函数,每次创建新视图时我都会再次使用它。
-
目标:我需要在一个函数中找到一种方法来实现这一点,该函数将这种转换推广到所有
#usages example def post(self,request,*args,**kwargs): alert_date = request.data.get('alert_date') if alert_date: request.data['alert_date'] = localize(request.user.timezone,alert_date,'datetime')
、dateField
和timeField
字段。 - 我尝试过:创建一个类视图并在其中使用这些函数,然后为每个新应用覆盖该类。但是,每个模型都有名称不同的日期字段,很难做出一个灵活的函数来识别哪些字段需要本地化或规范化。
注意:我的意思是标准化将时区从 UTC 转换为当前登录用户时区。
解决方法
如 DRF 中 DateTimeField
的 documentation 所述,它有一个参数 default_timezone
:
default_timezone
- 代表时区的 pytz.timezone
。如果
未指定且启用了 USE_TZ
设置,默认为
current
timezone。
如果 USE_TZ
被禁用,则日期时间对象将是幼稚的。
正如它所描述的,只要您设置了 USE_TZ
,该字段将自动使用当前时区。这当然意味着您必须以某种方式设置 (activate
[Django docs]) 当前时区。 Django 的 documentation 也有一些示例代码,它使用会话来存储时区和一个中间件来设置它,尽管似乎您将时区存储在用户对象本身中,因此您可以编写一个使用它的中间件。此外,由于您使用 DRF 进行身份验证,它实际上是在视图层进行身份验证,因此中间件并不真正具有经过身份验证的用户,由于您使用的是 rest_framework_simplejwt
,因此您可以使用 this question 中描述的解决方法:
import pytz
from django.utils import timezone
from rest_framework_simplejwt import authentication
class TimezoneMiddleware:
def __init__(self,get_response):
self.get_response = get_response
def __call__(self,request):
tzname = None
user = self.get_request_user(request)
if user:
tzname = user.timezone
if tzname:
timezone.activate(pytz.timezone(tzname))
else:
timezone.deactivate()
return self.get_response(request)
def get_request_user(self,request):
try:
return authentication.JWTAuthentication().authenticate(request)[0]
except:
return None
将此中间件添加到 MIDDLEWARE
中 settings.py
之后的 AuthenticationMiddleware
列表中,理想情况下最后应该可以工作:
MIDDLEWARE = [
...
'path.to.TimezoneMiddleware',]
虽然上述解决方案一开始看起来不错,但后来变成需要使用变通方法。更好的方法是使用可以设置当前时区的 mixin。我们的 mixin 来完成它的任务的一个好点是 initial
类的 ApiView
方法,这个方法在实际视图方法 (get
,post
等)被调用,因此它适合我们的需要:
import pytz
from django.utils import timezone
class TimezoneMixin:
def initial(self,request,*args,**kwargs):
super().initial(request,**kwargs)
tzname = None
if request.user.is_authenticated:
tzname = request.user.timezone
if tzname:
timezone.activate(pytz.timezone(tzname))
else:
timezone.deactivate()
class YourView(TimezoneMixin,SomeViewClassFromDRF):
...
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。