Django-tastypie: Campos diferentes para as ações SHOW e INDEX

Criado em 25 out. 2010  ·  40Comentários  ·  Fonte: django-tastypie/django-tastypie

Oi pessoal, é possível exibir campos diferentes para o recurso dependendo da ação.

O motivo pelo qual desejo é evitar sobrecarregar a ação do índice com todos os campos has_many que o recurso inclui.

Por exemplo, na visualização do índice do Autor, gostaria de retornar apenas seu nome, mas para a ação de exibição, gostaria de incluir todos os recursos de livros.

obrigado,
Bojan

documentation feature

Comentários muito úteis

Uma solução simples que estou usando é substituir os métodos get_detail & get_list para editar esses campos.
Isso economiza a sobrecarga de realmente buscar os dados para o campo e, em seguida, excluí-los do pacote, mas não tenho certeza se esse método é threadsafe, pois parece que os objetos Resource não são criados em todas as chamadas de API.
Será ótimo se alguém puder comentar sobre isso.

Aqui está o código:

class ArticleResource(BaseModelResource):
    owner = fields.ToOneField(UserResource, 'owner', full=True)

    class Meta:
        resource_name = "articles"

    def get_list(self, request, **kwargs):
        self.fields.pop("comments", None)
        return super(ArticleResource, self).get_list(request, **kwargs)

    def get_detail(self, request, **kwargs):
        self.fields["comments"] = fields.ToManyField(CommentResource, 'comments', full=True)
        return super(ArticleResource, self).get_detail(request, **kwargs)

Todos 40 comentários

+1
Espero profundamente por este recurso: "campo de detalhes" e "campo de lista" podem ser separados

+1 (via duplicado em # 44)

Parece-me que há duas coisas que poderiam facilitar a obtenção de subconjuntos de dados em ações de índice:

  1. Faça full_dehydrate usar campos diferentes para métodos de índice e get. Talvez algo como:

Meta:
index_exclude_fields = ['some_m2m_field']

Isso nos permitiria salvar algumas consultas de banco de dados.

  1. Passe o parâmetro 'método' adicional para desidratar. Isso permitiria a personalização do pacote como pular alguns campos que não são necessários no índice e queremos poupar um pouco de largura de banda.

Não avaliei 1. teria impacto no cache.
O que você acha?

Também enganado em # 48, mas cada problema tem aspectos valiosos.

Eu queria empurrar esse recurso se pudesse, mas estou com falta de armas. Ele precisará estar lá para melhor suporte de arquivo de qualquer maneira, então tem que ser resolvido. Terá como alvo 1.0 nisso.

Depois de examinar meu caso de uso um pouco mais, gostaria de sugerir que fosse implementado como algo mais flexível do que apenas mostrar / listar visualizações. O que eu realmente preciso fazer é pegar um sinalizador de querystring como este:

& forma = [completo | simples | nenhum]

e fazer com que a saída reflita o nível de detalhe selecionado pelo usuário. Procurei uma maneira de hackear isso, mas como os campos são incluídos / excluídos quando a classe ModelResource é criada, não consegui pensar em nenhuma maneira de alterar o que estava disponível posteriormente no ciclo de resposta. Além disso, o objeto de solicitação não está disponível nas etapas do processo em que eu gostaria de fazer tal determinação.

+1

Mencionarei esse pensamento aqui primeiro, pois parece relevante para este problema, mas pode precisar ser desenvolvido por conta própria.

Se um mecanismo para mostrar campos diferentes for desenvolvido, seria útil se pudesse ser integrado também à autenticação. Ser capaz de expor mais / menos campos depende das permissões do usuário seria muito poderoso. Se isso já é possível, não consegui encontrar nenhuma menção a isso.

+1

+1

+1

+1

+1

+1

+1

Eu esperaria ser capaz de controlar os campos exibidos (usando campos ou exclusões) em um método por (POST, PUT, GET), além de lista / detalhe.

Aqui está uma solução alternativa, veja o exemplo abaixo: você não exclui nada do modelo de recursos do modelo original (UserResource no exemplo), portanto, ele estaria completo em sua exibição de índice. Você tem que ir para o método de desidratar do modelresource que inclui seu submodelo (o BlogPostResource inclui o autor nele) e apenas deletar os elementos do bundle.

exemplo:

class BlogPostResource(ModelResource):
     author = fields.ForeignKey(UserResource, 'author', full=True)
     ....
     class Meta:
         ...

def dehydrate(self, bundle):
         del bundle.data['author'].data['field_you_dont_wanna_show_here']
         del bundle.data['author'].data['field_you_dont_wanna_show_here']
         return bundle

Então, quando você deseja listar os usuários, você ainda obtém todos os campos, mas ao listar postagens de blog, você pode, por exemplo, obter apenas o nome e o sobrenome do autor.

O que você acha?

Exemplo de uma solução muito, muito suja: não mostra os campos do projeto em list_view, mas mostra em detail_view, sem ter que acessar o próprio recurso, obtendo o próprio url sem codificá-lo deve ser possível, mas não tive tempo de verificá-lo :

class CompanyResource(ModelResource):
       """
       Tastypie resource for Company
      """
       projects = fields.ToManyField('api.resources.ProjectResource',
                                  'projects',full=True)
       class Meta:
           queryset = Company.objects.all()
           resource_name = 'companies'

       def dehydrate(self, bundle):
           if bundle.request.path == "/api/v1/companies/":
               del bundle.data['projects']
           return bundle

+1

+1

+1

Também vou embarcar no seria bom ter trem, mas o que @ashwoods apontou é o suficiente para me

    def dehydrate(self, bundle):
        if self.get_resource_uri(bundle) == bundle.request.path:
            print "Detail"

        if self.get_resource_uri(bundle) != bundle.request.path:
            print "Not Detail - Could be list or reverse relationship."

        return bundle

Então, pensei mais um pouco e descobri algo que chega bem perto de me deixar fazer o que acho que @bmihelac estava procurando.

Usando o exemplo @ashwoods fornecido, suponha que só quiséssemos exibir o campo de projetos se fosse uma resposta detalhada:

class CompanyResource(ModelResource):
    """
    Tastypie resource for Company
    """

    class Meta:
        queryset = Company.objects.all()
        resource_name = 'companies'
        additional_detail_fields = {'projects': fields.ToManyField('api.resources.ProjectResource', 'projects',full=True)}

    def dehydrate(self, bundle):
        # detect if detail
        if self.get_resource_uri(bundle) == bundle.request.path:
            # detail detected, include additional fields
            bundle = self.detail_dehydrate(bundle)

        return bundle

    # detail_dehydrate is basically full_dehydrate
    # except we'll loop over the additional_detail_fields
    # and we won't want to do the dehydrate(bundle) at the end
    def detail_dehydrate(self, bundle):
        """
        Given a bundle with an object instance, extract the information from it
        to populate the resource.
        """
        # Dehydrate each field.
        # loop over additional_detail_fields instead
        #for field_name, field_object in self.fields.items():
        for field_name, field_object in self._meta.additional_detail_fields.items():
            # A touch leaky but it makes URI resolution work.
            if getattr(field_object, 'dehydrated_type', None) == 'related':
                field_object.api_name = self._meta.api_name
                field_object.resource_name = self._meta.resource_name

            bundle.data[field_name] = field_object.dehydrate(bundle)

            # Check for an optional method to do further dehydration.
            method = getattr(self, "dehydrate_%s" % field_name, None)

            if method:
                bundle.data[field_name] = method(bundle)

        # dehydrating the bundle will create an infinite loop
        #bundle = self.dehydrate(bundle)
        return bundle

+1, usando correção de @dericcrago por enquanto.

+1

+1

Implementação parcial em # 526, não tenho certeza se estou convencido de tudo isso e não tem testes / documentos.

Acabei de ver este bilhete ... e também gosto da abordagem 'forma' mencionada por onyxfish acima ...

Estava pensando que minha solução no # 526 era um pouco limitada, caso as pessoas quisessem 'formas' diferentes em outros casos ...

às sugestões para remover campos após a desidratação ... meu único motivo é evitar computar os valores em primeiro lugar.

No entanto, a ideia do gancho detail_dehydrate permite adicionar condicionalmente mais detalhes, eu gosto.

Parece que duas implementações possíveis, incluindo testes e documentos, estão disponíveis. Escrevi um em # 569 e # 538 também executa uma funcionalidade semelhante (# 538 permitindo um pouco mais de flexibilidade, pois use_in pode ser chamado). Minha implementação adiciona atributos meta para controlar esta funcionalidade (que é consistente com o atributo fields atual) enquanto # 538 adiciona um atributo aos campos. Ambos parecem válidos, apenas uma decisão de design quanto ao caminho a seguir. Adicionar ao meta parece consistente para mim e é mais fácil de usar, visto que alguns campos podem ser gerados automaticamente e modificar seus parâmetros de inicialização pode não ser possível. Outra alternativa seria combinar as duas solicitações de pull e permitir que o parâmetro use_in seja definido automaticamente com base no atributo meta ; no entanto, isso parece adicionar mais complexidade à API do que o necessário. Obrigado a @issackelly por apontar solicitações pull relacionadas para mim.

[concordando que eu era a causa original por trás do # 538, sendo uma limpeza do meu # 526]
Faz muito sentido ... a abordagem Meta seria, de fato, combinada com a lista de exclusões para ModelResource, etc ...

Como eu disse em outro tíquete, uma solução "simples" como essa seria, IMHO, suficiente para uma versão 1.0 ... com uma solução mais complexa como "'formas' selecionáveis ​​pelo cliente" sendo talvez desejável para uma versão posterior. .

@funkybob Concordo, certamente a solução mais complexa seria útil do lado do cliente, mas seria bom ter essa funcionalidade incluída o mais rápido possível para que possa começar a ser usada antes do lançamento do 1.0.

Na verdade, usando o PR em um aplicativo de produção, eu realmente gosto da flexibilidade do retorno de chamada fornecido pelo # 538. Tenho alguns casos de uso em que preciso ocultar um recurso em tempo de execução com base na permissão.

Isso não seria possível para mim usando # 569

Olá, alguma novidade? Essa RP torna a vida muito mais fácil, obrigado!

Indo com hack fornecido por @dericcrago

+1

Uma solução simples que estou usando é substituir os métodos get_detail & get_list para editar esses campos.
Isso economiza a sobrecarga de realmente buscar os dados para o campo e, em seguida, excluí-los do pacote, mas não tenho certeza se esse método é threadsafe, pois parece que os objetos Resource não são criados em todas as chamadas de API.
Será ótimo se alguém puder comentar sobre isso.

Aqui está o código:

class ArticleResource(BaseModelResource):
    owner = fields.ToOneField(UserResource, 'owner', full=True)

    class Meta:
        resource_name = "articles"

    def get_list(self, request, **kwargs):
        self.fields.pop("comments", None)
        return super(ArticleResource, self).get_list(request, **kwargs)

    def get_detail(self, request, **kwargs):
        self.fields["comments"] = fields.ToManyField(CommentResource, 'comments', full=True)
        return super(ArticleResource, self).get_detail(request, **kwargs)

+1

outra solução alternativa é ter diferentes recursos para detalhes e visualizações de lista:

from tastypie.resources import ModelResource
from django.contrib.auth.models import User

# detail will show everything except password
class UserResourceDetail(ModelResource):
    class Meta:
        queryset = User.objects.all()
    excludes = ('password',)
    resource_name = 'user'

# list will only show username & date_joined (and exclude password)
class UserResource(UserResourceDetail):
    class Meta(UserResourceDetail.Meta):
        fields = ('username', 'date_joined')
    get_detail = UserResourceDetail().get_detail

# ... register & use UserResource

+1

+1 para solução alternativa @dnozay

+1 @dnozay , incrível

Observe que se você quiser que get_resource_uri funcione corretamente com a visualização de detalhes do usuário, será necessário adicionar o seguinte após UserResource ser definido.

UserResourceDetail.get_resource_uri = UserResource().get_resource_uri

Caso contrário, resource_uri estará vazio em todas as respostas de detalhes.

Possivelmente relacionado: # 1265

Esta página foi útil?
0 / 5 - 0 avaliações