【问题标题】:Django: Get current user in model saveDjango:在模型保存中获取当前用户
【发布时间】:2012-06-15 00:40:54
【问题描述】:

我想在models.py的save方法中获取当前登录的用户(request.user)。我想检查一下用户的角色,看看它是否可以根据他的角色执行一些操作。

models.py:

class TimeSheet(models.Model):
    check_in_time = models.TimeField()
    check_out_time = models.TimeField()

class Tasks(models.Model):
    time_sheet = models.ForeignKey(TimeSheet)
    project = models.ForeignKey(Project)
    start_time = models.TimeField()
    end_time = models.TimeField()

    def save(self, *args,**kwargs):
        project = SpentTime.objects.get(project__project__id = self.project.id)
        start = datetime.datetime.strptime(str(self.start_time), '%H:%M:%S')
        end = datetime.datetime.strptime(str(self.end_time), '%H:%M:%S')
        time = float("{0:.2f}".format((end - start).seconds/3600.0))

        if common.isDesigner(request.user):
            SpentTime.objects.filter(project__project__id = self.project.id).update(design = float(project.design) + time)

        if common.isDeveloper(request.user):
            SpentTime.objects.filter(project__project__id = self.project.id).update(develop = float(project.develop) + time)

        super(Tasks, self).save(*args, **kwargs)

这里Tasks 模型被用作Timesheet 模型中的内联。我想检查当前登录用户的角色并根据用户的角色更新另一个模型。这里我需要request.user 来检查当前用户的角色。我没有使用任何表单或模板,而是完全使用 Django admin。有什么方法可以在save 方法中获取request.user 或者在admin.py 中检查和更新另一个模型中的值吗?

【问题讨论】:

    标签: python django django-models


    【解决方案1】:

    正确的方法是使用threading.local,而不是使用带有threading.current_thread 的字典,因为这会导致内存泄漏,因为只要应用程序运行,旧值就会一直保留在那里:

    import threading
    
    request_local = threading.local()
    
    def get_request():
        return getattr(request_local, 'request', None)
    
    class RequestMiddleware():
        def __init__(self, get_response):
            self.get_response = get_response
    
        def __call__(self, request):
            request_local.request = request
            return self.get_response(request)
    
        def process_exception(self, request, exception):
            request_local.request = None
    
        def process_template_response(self, request, response):
            request_local.request = None
            return response
    

    如果你想访问用户而不是请求,你可以做get_request().user,或者保存用户而不是__call__上的请求

    【讨论】:

    • 这个中间件应该在列表中尽快还是最后?还是根本不重要?
    • @karlosss 这并不重要,因为您已经可以访问中间件内部的 request 对象,因此您不会在那里使用 get_request,它不会修改请求
    【解决方案2】:

    您是否尝试过使用以下库:

    https://pypi.org/project/django-currentuser/

    网站上如何使用它的片段:

    from django_currentuser.middleware import (get_current_user, get_current_authenticated_user)
    # As model field:
    from django_currentuser.db.models import CurrentUserField
    class Foo(models.Model):
        created_by = CurrentUserField()
    

    【讨论】:

      【解决方案3】:

      您可以从另一个角度解决这个问题。您应该重写 AdminSites save_model 方法,而不是更改模型保存方法。正如您已经指出的那样,您将拥有请求对象并可以访问已登录的用户数据。

      查看文档的这一章:Django ModelAdmin documentation save_model

      【讨论】:

      • 谢谢詹斯。我试过了。由于我在这里使用内联,我无法使用管理员 save_model 完成它。但是我已经通过添加 created_by 用户外键字段来完成它。
      • 如果你使用内联,那么你使用save_formsetdocs.djangoproject.com/en/1.4/ref/contrib/admin/…
      • 不错的@ChrisPratt。不知道这个方法。下面几行还有一个方法save_related。也许这也能解决问题:docs.djangoproject.com/en/1.4/ref/contrib/admin/…
      • 对。实际上,如果您使用的是 Django 1.4,save_related 是您的最佳选择。不幸的是,我仍然被困在 Django 1.3.1 的土地上,save_related 还不存在。但是,无论哪种方式,save_formset 仍然可以用来完成任务。
      • 请注意,对于 django 1.10,这是docs.djangoproject.com/en/1.10/ref/contrib/admin/…
      【解决方案4】:

      @nKn 提出的解决方案是一个很好的起点,但是当我今天尝试实施时,我遇到了两个问题:

      1. 在当前 Django 版本中,作为普通对象创建的中间件不起作用。
      2. 单元测试失败(因为它们通常在单线程中运行,因此如果第一个测试有 HTTP 请求而第二个没有,您的“请求”可能会停留在两个后续测试之间)。

      这是我更新的中间件代码,它适用于 Django 1.10 并且不会破坏单元测试:

      from threading import current_thread
      
      from django.utils.deprecation import MiddlewareMixin
      
      
      _requests = {}
      
      
      def current_request():
          return _requests.get(current_thread().ident, None)
      
      
      class RequestMiddleware(MiddlewareMixin):
      
          def process_request(self, request):
              _requests[current_thread().ident] = request
      
          def process_response(self, request, response):
              # when response is ready, request should be flushed
              _requests.pop(current_thread().ident, None)
              return response
      
          def process_exception(self, request, exception):
              # if an exception has happened, request should be flushed too
               _requests.pop(current_thread().ident, None)
      

      【讨论】:

      • 您有测试示例吗?在我的测试中总是出现AttributeError:'NoneType'对象没有属性'user'?谢谢!
      • @Sascha Rau 不幸的是,只测试我的商业代码,对我的项目结构有一定的凝聚力。如果您尝试在实际未发出请求的代码中访问请求,那么您描述的行为是完全可以的。为了安全起见,试试request = current_request() ; if request is not None: ...
      【解决方案5】:

      我找到了一种方法来做到这一点,但它涉及声明一个中间件。在您的应用程序中创建一个名为 get_username.py 的文件,其中包含以下内容:

      from threading import current_thread
      
      _requests = {}
      
      def get_username():
          t = current_thread()
          if t not in _requests:
               return None
          return _requests[t]
      
      class RequestMiddleware(object):
          def process_request(self, request):
              _requests[current_thread()] = request
      

      编辑您的settings.py 并将其添加到MIDDLEWARE_CLASSES:

      MIDDLEWARE_CLASSES = (
          ...
          'yourapp.get_username.RequestMiddleware',
      )
      

      现在,在您的 save() 方法中,您可以像这样获取当前用户名:

      from get_username import get_username
      
      ...
      
      def save(self, *args, **kwargs):
          req = get_username()
          print "Your username is: %s" % (req.user)
      

      【讨论】:

      • 如果线程被杀死并一次又一次地创建,它可能会在一段时间内产生内存问题。您实际上应该添加 current_thread().user = user 并且应该以这种方式访问​​该属性。一旦线程被杀死,内存也会被释放。
      【解决方案6】:

      我不认为 save_model 方法覆盖是最好的选择。例如,假设您想要保存用户信息或根据用户信息验证模型,并且 save() 不是来自视图或管理站点本身。

      人们所问的是像这样的结构:

      def save(..)
          self.user = current_user()
      

      或

      def save(..)
          user = current_user()
          if user.group == 'XPTO':
              error('This user cannot edit this record')
      

      目前我发现的最佳方法是:

      https://bitbucket.org/q/django-current-user/overview

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-11-16
        • 2014-08-15
        • 1970-01-01
        • 2020-04-30
        • 2015-08-16
        • 2016-04-06
        相关资源
        最近更新 更多