Skip to content


Django, interceptando o autor do save

1. Introdução

Acredito que todos já tenham passado por esta situação utilizando o nosso querido framework django, que seria saber quem foi o autor de um lançamento, ou mesmo quem foi responsável por deletar uma determinada entidade critica.

Pois bem, quem já passou por esta situação, sabe bem que dentro do metodo django.db.models.Model.save e django.db.models.Model.delete não existe a possibilidade de se ter o User daquela sessão a menos que se use de truques mirabolates, neste artigo vamos aprender como fazer isto de uma forma bem limpa.

2. Criação dos modelos

Vamos usar no nosso exemplo um blog, que tem POSTs e estes podem ser publicados por usuários cadastrados em nosso sistema. Veja como ficaria este modelo.

from django.db import Models

class Post(models.Model):
   title = models.CharField(max_length = 60, unique = True)
   unique_identifier = models.CharField(max_length = 60, unique = True)
   user = models.ForeignKey('auth.User')
   content = models.TextField()

Desta forma o nosso modelo estaria pronto para ser usado, mas neste caso o nosso FORM teria um campo Usuário, que geraria um combo onde o usuário iria escolher um Usuário. Desta forma no máximo conseguiríamos fazer com que o combo só iniciasse com o usuário logado, mas isto esteticamente ficara feio e com cara de POG (Programação Orientada a Gambiarra).

3. A solução

A primeira modificação que deve ser feito neste modelo é dizer que o atributo user aceita branco mas não aceita nulo. E depois iremos precisar de uma implementação que foi comentada no Django CookBook, onde eles falam de ThreadlocalsAndUser, veja a implementação proposta:

# threadlocals middleware
try:
    from threading import local
except ImportError:
    from django.utils._threading_local import local

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

class ThreadLocals(object):
    """Middleware that gets various objects from the
    request object and saves them in thread local storage."""
    def process_request(self, request):
        _thread_locals.user = getattr(request, 'user', None)

Precisamos agora informar ao django para que ele use a nossa middleware, podemos conferir na documentação oficial como ativar o nosso middleware. Agora vamos as modificações no arquivo de models:

from django.db import models
from middleware.threadlocals import get_current_user

class Post(models.Model):
   title = models.CharField(max_length = 60, unique = True)
   unique_identifier = models.CharField(max_length = 60, unique = True)
   user = models.ForeignKey('auth.User', blank = True, null = False)
   content = models.TextField()

def save(force_insert = False, force_update = False):
   # Verifica se esta criando
   if self.pk is None:
      self.user = get_current_user()
   models.Model.save(self, force_insert, force_update)

Desta forma no momento em que o save for evocado, o primeiramente o django executar todas as middleware de inicio será evocado o método process_request desta forma será guardado o nosso usuário em _thread_locals.user depois será executado os códigos da view que será responsável por chamar o Post.save e desta forma será preenchido o campo user com o usuário da sessão, caso não tenha nenhum usuário logado no momento será gerada uma exceção, uma vez que não é permitido null em user.

Agora tome como desafio, interceptar quem foi que deletou o POST, ou melhor, somente permitir deletar o POST somente o administrador ou o próprio autor.

4. Referencias

  1. Django CookBook – http://code.djangoproject.com/wiki/CookBookThreadlocalsAndUser
  2. Django Project – http://docs.djangoproject.com/en/dev/topics/http/middleware/#activating-middleware

Posted in Desenvolvimento.

Tagged with , , , , .


2 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. Marcello Bontempo Salgueiro says

    Ola Rodrigo estava procurando algo na net para tentar fazer uma request quando qualquer model fosse salvo e cai aqui no seu blog… outra maneira também de se fazer isso é assim:
    class AdminQualquer(ModelAdmin):

    def save_model(self,request,obj, form,change):
    obj.user_own = request.user.username
    obj.save()
    qualquer coisa se não saiu a identação dá uma olhada no save_model aqui:
    http://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.save_model

    abracaos

  2. Rodrigo Pinheiro Matias says

    Muito bom, neste caso ficaria a dependência do admin do django, de toda forma é uma maneira boa de se resolver o problema.



Some HTML is OK

or, reply to this post via trackback.