通过DRY方法添加创建和修改的时间


72

有类似的东西

  • 由...制作
  • 创建日期
  • modified_by
  • 修改日期

对于许多表来说,这将是非常常见的模式。

1)您可以使用以下命令在model.py中自动设置创建日期(但不能设置其他日期)

created_date = models.DateTimeField(auto_now_add=True, editable=False)

2)您可以在model.py中创建/修改日期(但不能由/用户创建,因为没有请求上下文)

def save(self):
    if self.id:
        self.modified_date = datetime.now()
    else:
        self.created_date = datetime.now()
    super(MyModel,self).save()

3)您可以在admin.py中设置创建/修改日期和日期-但这不会处理非管理员更新

def save_model(self, request, obj, form, change):
    if change:
        obj.modified_by = request.user
        obj.modified_date = datetime.now()
    else:
        obj.created_by = request.user
        obj.created_date = datetime.now()
    obj.save()

4)最后一个位置是在view.py中,它可以完成所有4个操作,但不包括管理员更新。

因此,现实中必须分散逻辑,至少以3和4重复(或者模型中的方法从两者中调用,这将被遗漏)

有什么更好的方法?(我已经使用python / django了几天,所以很容易遗漏一些明显的东西)

  • 你能做一些像@login_required这样的东西吗,例如@audit_changes
  • 您可以访问模型中的请求和当前用户并在那里集中逻辑吗?


1
+1。我想看到djangoesque解决方案,将基于请求的模型代码集中在某个地方..最干净的方法是什么!
Yuji'Tomita'Tomita

Answers:


104

创建/修改日期现在可以由Django处理,因此可以像这样实现:

class BaseModel(models.Model):
    created_date = models.DateTimeField(auto_now_add=True)
    modified_date = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True

通过将其添加到抽象模型基类中,可以轻松地将其添加到应用程序的所有模型中。

由于request.user无法使用,因此难以存储用户。正如SeanOC所提到的,这是Web请求和模型层之间关注点的分离。您可以一直传递此字段,或存储request.user在threadlocal中。Django CMS针对其权限系统执行此操作。

class CurrentUserMiddleware(object):
    def process_request(self, request):
        set_current_user(getattr(request, 'user', None))

用户跟踪发生在其他地方:

from threading import local
_thread_locals = local()

def set_current_user(user):
    _thread_locals.user=user

def get_current_user():
    return getattr(_thread_locals, 'user', None)

对于非Web环境(例如管理命令),您必须set_current_user在脚本的开头调用。


这个答案解决了这个问题,但是如果它也讨论了线程局部变量的潜在问题,那就太好了。或此类讨论的链接。
Eldamir '16

1
我已经简单地谈到了这一点,但是我主要可以指出,它可以打破关注点的分离,并在整个应用程序中公开敏感数据(不确定第二部分是否构成真正的风险)。对全局变量的所有担忧也适用。
vdboor '16

1
在Django == 1.10.2中使用了它,这破坏了中间件。您必须使用根据此django链接class CurrentUserMiddleware(MiddlewareMixin):导入的意思from django.utils.deprecation import MiddlewareMixin
lukik

注:auto_now不触发model.update
user66081 '19

10

对于带有时间戳的模型,您可能希望查看django-model-utilsdjango-extensions。它们每个都包含抽象基类,这些基类可自动处理创建的和最后修改的时间戳。您可以直接使用这些工具,也可以查看它们如何解决问题并提出自己的解决方案。

至于您的其他问题:

你能做一些像@login_required这样的东西吗,例如@audit_changes

可能是的,但是您必须非常小心以确保线程安全。您可能可以做的是在@audit_changes装饰器中,设置一个标志以在threadlocal中启用审核。然后,无论是在模型的save方法中还是在信号处理程序中,您都可以检查审计标记并记录审计信息(如果已设置标记)。

您可以访问模型中的请求和当前用户并在那里集中逻辑吗?

是的,但您会做出权衡。正如您所涉及的那样,Django的ORM与请求/身份验证处理位之间的关注点非常明显且有意分离。有两种方法可以从请求(当前用户)到ORM(您的模型)获取信息。您可以手动管理对象上创建者/修改者信息的更新,也可以设置一种机制来自动处理维护工作。如果您采用手动方法(将信息从视图中的请求通过方法调用传递给ORM),则将需要更多的代码来维护/测试,但您需要将关注点分离。使用手动方法,如果您不得不在请求/响应周期之外使用对象(例如,cron脚本,延迟任务,交互式外壳)。如果您可以打破关注点分离的问题,则可以进行以下设置:在中间件中与当前用户一起设置本地线程,然后在模型的save方法中查看该本地线程。与手动方法相反,您需要处理的代码更少,但是如果您想在请求/响应周期之外使用对象,则将花费更多的时间。此外,您将必须非常小心,以采用自动化程度更高的方法来确保所有线程安全。与手动方法相反,您需要处理的代码更少,但是如果您想在请求/响应周期之外使用对象,则将花费更多的时间。此外,您将必须非常小心,以采用自动化程度更高的方法来确保所有线程安全。与手动方法相反,您需要处理的代码更少,但是如果您想在请求/响应周期之外使用对象,则将花费更多的时间。此外,您将必须非常小心,以采用自动化程度更高的方法来确保所有线程安全。


-2

您可以导入User模型对象并调用get_current()吗?

另外,我认为您可以在admin.py中调用视图。


get_current适用于Django的站点框架,不用于身份验证。如果不将请求对象传递到范围内,将无法帮助您获取当前用户。
SeanOC
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.