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

2024-03-23

  • Intro: 我的项​​目TIME_ZONE等于'UTC'而我的用户来自太多时区。所以,当我用户 makePOST or PUT with date or time or dateTime字段我将这些字段转换为UTC before 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, date, 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')
  • Problem:我在太多视图中使用了这些功能,每次创建新视图时我都会再次使用它。
  • Goal:我需要找到一种方法在一个函数中做到这一点,将这种转换推广到所有人dateField, timeField and datetimeField fields.
  • I tried:创建一个类视图并在其中使用这些函数,然后为每个新应用程序覆盖该类。但是,每个模型都有不同名称的日期字段,很难创建一个灵活的函数来识别哪个字段需要本地化或标准化。

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


如中所述文档 https://www.django-rest-framework.org/api-guide/fields/#datetimefield for DateTimeField在DRF中,它有一个参数default_timezone:

default_timezone - A pytz.timezone代表时区。如果 未指定且USE_TZ设置已启用,默认为 这当前的 时区 https://docs.djangoproject.com/en/stable/topics/i18n/timezones/#default-time-zone-and-current-time-zone. If USE_TZ被禁用,那么日期时间对象将是幼稚的。

正如它也描述的那样,只要你设置了USE_TZ该字段将自动使用当前时区。这当然意味着您必须设置(activate[Django 文档] https://docs.djangoproject.com/en/3.2/ref/utils/#django.utils.timezone.activate)以某种方式当前时区。姜戈的文档 https://docs.djangoproject.com/en/3.2/topics/i18n/timezones/#selecting-the-current-time-zone还有一些示例代码,使用会话来存储时区和中间件来设置它,尽管您似乎将时区存储在用户对象本身中,因此您可以编写一个使用它的中间件。另外,由于您使用 DRF 进行身份验证,它实际上在视图层上进行身份验证,因此中间件实际上并没有经过身份验证的用户,因为您正在使用rest_framework_simplejwt您可以使用中描述的解决方法这个问题 https://stackoverflow.com/q/54945856/14991864:

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, *args, **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):
    ...
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 django DRF 中处理时区而不重复太多? 的相关文章

随机推荐