Python 添加创建/修改时间的 DRY 方式

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4754485/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 17:22:32  来源:igfitidea点击:

DRY way to add created/modified by and time

pythondjango

提问by Ryan

Having something like

有类似的东西

  • created_by
  • created_date
  • modified_by
  • modified_date
  • 由...制作
  • 创建日期
  • 修改者
  • 修改日期

Would be a very common pattern for a lot of tables.

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

1) You can set created date automatically (but not others) in model.py with

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

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

2) You could do created/modified dates (but not by/user as don't have request context) in model.py with

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) You could set the created/modifed date and by in admin.py - but this doesn't deal with non admin updates

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

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) And the final place would be in the view.py which can do all 4, but doesn't cover admin updates.

4) 最后一个地方是 view.py,它可以完成所有 4 个,但不包括管理更新。

So realistically have to have logic spread out, at a minimum repeated in 3 & 4 (or a method on the model called from both, which will be missed)

因此,实际上必须将逻辑展开,至少在 3 和 4 中重复(或从两者调用的模型上的方法,这将被遗漏)

Whats a better way? (I've been working with python/django for a couple of days so could easily be missing something obvious)

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

  • Can you do someting like @login_required e.g. @audit_changes
  • Can you get access to the request and current user in the model and centralise logic there?
  • 你能做一些像@login_required 这样的事情,比如@audit_changes
  • 您能否访问模型中的请求和当前用户并将逻辑集中在那里?

采纳答案by vdboor

The create/modification dates can be handled by Django now, so they can be implemented like:

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

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

    class Meta:
        abstract = True

By adding this to a abstract model base class, it can be easily added to all models of the application.

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

Storing the user is harder, since the request.useris not available. As SeanOC mentioned, this is a separation of concerns between the web request, and model layer. Either you pass this field all the time, or store request.userin a threadlocal. Django CMS does this for their permission system.

存储用户更难,因为request.user不可用。正如 SeanOC 所提到的,这是 Web 请求和模型层之间关注点的分离。要么一直传递这个字段,要么存储request.user在线程本地。Django CMS 为他们的权限系统执行此操作。

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

And the user tracking happens elsewhere:

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

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)

For non-web environments (e.g. management commands), you'd have to call set_current_userat the start of the script.

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

回答by Timbadu

Can you import the User model object and call get_current()?

你能导入 User 模型对象并调用 get_current() 吗?

Also, I think you can call views in the admin.py.

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

回答by SeanOC

For timestamped models you probably want to look at django-model-utilsor django-extensions. They each include abstract base classes which automatically handle of a created and last modified timestamp. You can either use these tools directly or look at how they solved the problem and come up with your own solution.

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

As for your other questions:

至于你的其他问题:

Can you do someting like @login_required e.g. @audit_changes

你能做一些像@login_required 这样的事情,比如@audit_changes

Potentially yes but you'd have to be very careful to keep things thread-safe. What you potentially could do is in your @audit_changes decorator, set a flag to enable auditing in a threadlocal. Then either in the save method of your models or in a signal handler, you could check for your audit flag and record your audit info if the flag had been set.

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

Can you get access to the request and current user in the model and centralise logic there?

您能否访问模型中的请求和当前用户并将逻辑集中在那里?

Yes, but you'll be making a tradeoff. As you've touched on a little bit, there is a very clear and intentional separation of concerns between Django's ORM and it's request/authentication handling bits. There are two ways ways to get information from the request (the current user) to the ORM (your model(s)). You can manually manage updating the creator/modifier information on your objects or you can set up a mechanism to automatically handle that maintenance work. If you take the manual approach (passing the information through method calls from the request in the view to the ORM), it will be more code to maintain/test but you keep the separation of concerns in place. With the manual approach, you will be in much better shape if you ever have to work with your objects outside of the request/response cycle (e.g. cron-scripts, delayed tasks, interactive shell). If you are ok with breaking down that separation of concerns, then you could setup something where you set a thread local with the current user in a middleware and then look at that thread local in the save method of your model. Converse to the manual approach, you'll have less code to deal with but you'll have a much harder time if you ever want to work with your objects outside of the request/response cycle. Additionally, you will have to be very careful to keep everything thread-safe with the more automated approach.

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