terça-feira, 4 de dezembro de 2012

Django: Managers e Querysets

Uma das coisas que eu mais uso nos models do Django são os Managers, aquelas classes que adicionam funções aos `.objects.sua_funcao_aqui` (ou até mesmo, criar outro "objects").

O único problema, era que essa função estava restrita à chamadas via `.objects`, ou seja, enquanto é lindo fazer:

Post.objects.da_semana()

Não era possível fazer:

Comment.objects.da_semana().aprovados()

Não era. (Mentira, sempre foi, eu é quem não sabia fazer)

A solução surgiu em um post no DjangoSnippets.org. E era muito mais simples do que eu esperava.

Da mesma forma que criamos novos Managers para Models, podemos criar Querysets para Managers. Simples assim:

# -*- coding: utf-8 -*-
from django.db import models
from django.db.models.query import QuerySet

class BlogQueryset(QuerySet):
    u'''Aqui nós definimos as funções que retornarão o que 
    queremos

    Você pode retornar qualquer coisa da função, mas tenha 
    em mente que se você não retornar um QuerySet, não será
    possível continuar o encadeamento de filter/order_by/etc
    '''
    def da_semana(self):
        return self.filter(created_at__gt=...)
    def aprovados(self):
        return self.filter(published=True)


class BlogManager(models.Manager):
    u'''Aqui nós precisamos repetir as funções, mas apenas 
    suas definições.
    
    O corpo da função é apenas a chamada para a função no 
    QuerySet, é exatamente assim que as funções filter, 
    exclude, order_by, etc, são feitas no Manager padrão 
    do Django
    
https://github.com/django/django/blob/master/django/db/models/manager.py
    '''
    def da_semana(self):
        return self.get_query_set().da_semana()
    def aprovados(self):
        return self.get_query_set().aprovados()
    def get_query_set(self):
        '''É aqui onde a mágica acontece, pra que toda essa 
        história funcione, você precisa que seja retornada 
        uma instancia do QuerySet que nós criamos'''
        return BlogQuerySet(self.model, using=self._db)
        # Particularmente eu acho que seria mais interessante 
        # usar self.queryset_class, assim não seria preciso 
        # ficar reescrevendo essa função desnecessariamente.
        # Vou tentar alterar no branch de desenvolvimento e 
        # se passar em todos os testes eu faço um pull request

class Post(models.Model):
    # Aqui vem seus campos como geralmente faria
    # ... 
    published = models.BooleanField(db_index=True)
    created_at = models.DateTimeField(db_index=True, auto_now_add=True)
    objects = BlogManager()

Com isso você já pode encadear suas chamadas aos seus filtros :)

pony powered

Nenhum comentário:

Postar um comentário