如何在 django DRF 中处理时区而不重复太多? [英] How to handling timezones in django DRF without repeating myself too much?

查看:35
本文介绍了如何在 django DRF 中处理时区而不重复太多?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  • 简介:我的项目 TIME_ZONE 等于 'UTC',而我的用户来自太多时区.因此,当我使用 datetimedateTime 制作 POSTPUT 时字段 我在 serializer.save() 之前将这些字段转换为 UTC.然后,当用户发出 GET 请求时,我将相同的字段转换回用户的时区,即 request.user.timezone
  • Intro: My project TIME_ZONE is equal to 'UTC' while I have users from too many time zones. So, when I user make POST or PUT with date or time or dateTime fields I convert these fields to UTC before serializer.save(). Then, when a user make a GET request I convert the same fields back to the timezone of the user which is 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')

  • 问题:我在太多视图中使用了这些函数,每次创建新视图时我都会再次使用它.
  • 目标:我需要在一个函数中找到一种方法来实现所有dateFieldtimeField的转换>datetimeField 字段.
  • 我尝试过:创建一个类视图并在其中使用这些函数,然后为每个新应用覆盖该类.但是,每个模型都有名称不同的日期字段,很难做出一个灵活的函数来识别哪些字段需要本地化或规范化.
    • Problem: I used these function in too many views and every time I create a new view I use it again.
    • Goal: I need to find a way to do that in one function that generalize this conversion for all dateField, timeField and datetimeField fields.
    • I tried: to make a class view and use these functions inside it then override that class for each new app. But, each model have date fields with different names and it was hard to make a flexible function that recognize which field need to be localized or normalized.
    • 注意:我的意思是标准化将时区从 UTC 转换为当前登录用户时区.

      Note: I mean by normalized to convert the timezone from UTC the the current login-in user timezone.

      推荐答案

      DateTimeField 的 ="nofollow noreferrer">文档,它有一个参数 default_timezone:

      As described in the documentation for DateTimeField in DRF, it has an argument default_timezone:

      default_timezone - 代表时区的 pytz.timezone.如果未指定且 USE_TZ 设置已启用,默认为当前时区.如果 USE_TZ 被禁用,那么日期时间对象将是幼稚的.

      default_timezone - A pytz.timezone representing the timezone. If not specified and the USE_TZ setting is enabled, this defaults to the current timezone. If USE_TZ is disabled, then datetime objects will be naive.

      正如它所描述的,只要您设置了 USE_TZ,该字段将自动使用当前时区.这当然意味着您必须设置 (activate [Django 文档]) 当前时区.Django 的文档有一些示例代码使用会话来存储时区和一个中间件来设置它,尽管您似乎将时区存储在用户对象本身中,因此您可以编写一个使用它的中间件.此外,由于您使用 DRF 进行身份验证,它实际上是在视图层上进行身份验证,因此中间件实际上并没有经过身份验证的用户,由于您使用的是 rest_framework_simplejwt,您可以使用 这个问题:

      As it also describes as long as you have set USE_TZ the field will automatically use the current timezone. This of course means you would have to set (activate [Django docs]) the current timezone somehow. Django's documentation also has some sample code that uses sessions for storing the timezone and a middleware to set it, although it seems you store the timezone in the user object itself so you can write a middleware that uses that instead. Also since you use DRF for authentication, it actually does the authentication on the view layer, so the middleware does not really have the authenticated user, Since you are using rest_framework_simplejwt you can use the workaround described in 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
      

      将此中间件添加到 settings.pyAuthenticationMiddleware 之后的 MIDDLEWARE 列表中,理想情况下最后应该可以工作:

      Add this middleware to the MIDDLEWARE list in settings.py somewhere after AuthenticationMiddleware, ideally at the last should work:

      MIDDLEWARE = [
          ...
          'path.to.TimezoneMiddleware',
      ]
      

      虽然上述解决方案一开始看起来不错,但后来变成需要使用变通方法.更好的方法是使用设置当前时区的 mixin.让我们的 mixin 完成它的任务的一个好点是 ApiView 类的 initial 方法,这个方法在实际的视图方法之前被调用(getpost 等)被调用,因此它适合我们的需要:

      Although the above solution seemed good at the start, it later turned to needing to use a workaround. A better way would be to use a mixin that would set the current timezone. A good point for our mixin to do it's task would be the initial method of the ApiView class, this method is called just before the actual view method (get, post, etc.) is called so it suits our needs:

      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):
          ...
      

      这篇关于如何在 django DRF 中处理时区而不重复太多?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆