Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
164 views
in Technique[技术] by (71.8m points)

python - DRY way to add created/modified by and time

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

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

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

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.

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)

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

  • 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?
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

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.user is 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.user in a threadlocal. Django CMS does this for their permission system.

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_user at the start of the script.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...