微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在 django DRF 中处理时区而不重复太多?

如何解决如何在 django DRF 中处理时区而不重复太多?

  • 简介:我的项目 TIME_ZONE 等于 'UTC',而我的用户来自太多时区。因此,当我使用 POSTPUTdate 字段创建 timedateTime 时,我在 {{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') dateFieldtimeField 字段。
  • 我尝试过:创建一个类视图并在其中使用这些函数,然后为每个新应用覆盖该类。但是,每个模型都有名称不同的日期字段,很难做出一个灵活的函数来识别哪些字段需要本地化或规范化。

注意:我的意思是标准化将时区从 UTC 转换为当前登录用户时区。

解决方法

如 DRF 中 DateTimeFielddocumentation 所述,它有一个参数 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

将此中间件添加到 MIDDLEWAREsettings.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 举报,一经查实,本站将立刻删除。