Django-tastypie: Diferentes campos para acciones SHOW e INDEX

Creado en 25 oct. 2010  ·  40Comentarios  ·  Fuente: django-tastypie/django-tastypie

Hola a todos, ¿es posible mostrar diferentes campos de recursos dependiendo de la acción?

La razón por la que lo quiero es evitar la carga de la acción del índice con todos los campos has_many que incluye el recurso.

Por ejemplo, en la vista de índice de Autor, me gustaría devolver solo su nombre, pero para mostrar la acción, me gustaría incluir todos los recursos de los libros.

Gracias,
Bojan

documentation feature

Comentario más útil

Una solución alternativa simple que estoy usando es anular los métodos get_detail & get_list para editar dichos campos.
Esto ahorra la sobrecarga de obtener los datos para el campo y luego eliminarlos del paquete, pero no estoy seguro de si este método es seguro para subprocesos, ya que parece que los objetos de recursos no se crean en cada llamada a la API.
Sería genial si alguien pudiera comentar sobre esto.

Aquí está el 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 comentarios

+1
Espero profundamente esta función: "campo de detalle" y "campo de lista" se pueden separar

+1 (por duplicado en # 44)

Me parece que hay dos cosas que podrían facilitar la obtención de subconjuntos de datos en acciones de índice:

  1. Haga que full_dehydrate use diferentes campos para los métodos index y get. Quizás algo como:

Meta:
index_exclude_fields = ['algunos_m2m_field']

Esto nos permitiría guardar alguna consulta de base de datos.

  1. Pase el parámetro de 'método' adicional para deshidratar. Esto permitiría la personalización del paquete como para omitir algunos campos que no son necesarios en el índice y queremos ahorrar algo de ancho de banda.

No evalué 1. Tendría impacto en el almacenamiento en caché.
¿Qué piensas?

También engañado en el # 48, pero cada tema tiene aspectos que valen la pena.

Quería eliminar esta función si pudiera, pero estoy muy superado. Tendrá que estar ahí para un mejor soporte de archivos de todos modos, por lo que debe abordarse. Apuntará a 1.0 en esto.

Después de examinar un poco más mi caso de uso para esto, me gustaría sugerir que se implemente como algo más flexible que solo mostrar / enumerar vistas. Lo que realmente necesito poder hacer es tomar una bandera de cadena de consulta como esta:

& shape = [completo | simple | ninguno]

y hacer que la salida refleje el nivel de detalle seleccionado por el usuario. Busqué una forma de piratear esto, pero como los campos se incluyen / excluyen cuando se crea la clase ModelResource, no pude encontrar ninguna forma de alterar lo que estaba disponible más adelante en el ciclo de respuesta. Además, el objeto de solicitud no está disponible en los pasos del proceso en los que me gustaría tomar tal determinación.

+1

Mencionaré este pensamiento aquí primero, ya que parece relevante para este problema, pero es posible que deba dividirse por sí solo.

Si se desarrolla un mecanismo para mostrar diferentes campos, sería útil si también se pudiera integrar con la autenticación. Ser capaz de exponer más / menos campos depende de los permisos del usuario sería muy poderoso. Si esto ya es posible, no he podido encontrar ninguna mención al respecto.

+1

+1

+1

+1

+1

+1

+1

Esperaría poder controlar los campos que se muestran (utilizando campos o excluidos) en un método por (POST, PUT, GET) además de la lista / detalle.

Aquí hay una solución alternativa, vea el ejemplo a continuación: no excluye nada del modelo de recursos del modelo original (UserResource en el ejemplo), por lo que estaría completo en su vista de índice. Tienes que entrar en el método de deshidratar del recurso modelo que incluye tu submodelo (el BlogPostResource incluye al autor en él) y simplemente eliminar los elementos del paquete.

ejemplo:

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

Entonces, cuando desee enumerar los usuarios, seguirá obteniendo todos los campos, pero cuando enumere las publicaciones de blog, puede, por ejemplo, obtener el nombre y apellido del autor.

¿Qué piensas?

Ejemplo de una solución muy, muy sucia: no muestra los campos del proyecto en list_view, pero sí en detail_view, sin tener que acceder al recurso en sí, obtener la URL sin codificarlo debería ser posible, pero no he tenido tiempo de verificarlo :

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

También me subiré a bordo. Sería bueno tener un tren, pero lo que @ashwoods señaló es suficiente para pasar. Lo modifiqué ligeramente para que sea un poco más dinámico.

    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

Así que pensé un poco más en esto y se me ocurrió algo que se acerca bastante a permitirme hacer lo que creo que @bmihelac estaba buscando.

Usando el ejemplo proporcionado por @ashwoods , suponga que solo queremos mostrar el campo de proyectos si es una respuesta detallada:

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 corrección de @dericcrago por ahora.

+1

+1

Implementación parcial en el n. ° 526, no estoy seguro de estar vendido en todo y carece de pruebas / documentos.

Acabo de ver este boleto ... y también me gusta el enfoque de 'forma' mencionado por onyxfish arriba ...

Estaba pensando que mi solución en el n. ° 526 era un poco limitada, en caso de que la gente quisiera diferentes 'formas' en otros casos ...

a las sugerencias de eliminar campos después de la deshidratación ... mi única razón es evitar calcular los valores en primer lugar.

Sin embargo, me gusta la idea de que el gancho detail_dehydrate permita agregar condicionalmente más detalles.

Parece que hay dos posibles implementaciones que incluyen pruebas y documentos disponibles. Escribí uno en el n. ° 569 y el n. ° 538 también realiza una funcionalidad similar (el n. ° 538 permite un poco más de flexibilidad ya que use_in puede ser invocable). Mi implementación agrega atributos meta para controlar esta funcionalidad (que es consistente con el atributo actual fields ) mientras que # 538 agrega un atributo a los campos. Ambos parecen válidos, solo una decisión de diseño sobre qué camino tomar. Agregar al meta me parece consistente y es más fácil de usar dado que algunos campos se pueden generar automáticamente y es posible que no se puedan modificar sus parámetros de inicialización. Otra alternativa sería combinar ambas solicitudes de extracción y permitir que el parámetro use_in se establezca automáticamente en función del atributo meta , sin embargo, esto parece agregar más complejidad a la API de lo necesario. Gracias a @issackelly por señalarme solicitudes de extracción relacionadas.

[repitiendo que yo era la causa original detrás del # 538, siendo una limpieza de mi # 526]
Tiene mucho sentido ... el enfoque Meta, de hecho, encajaría con la lista de exclusiones para ModelResource, etc.

Como dije en otro ticket, una solución "simple" como esta sería, en mi humilde opinión, suficiente para una versión 1.0 ... con una solución más compleja como "formas" seleccionables por el cliente "quizás deseable para una versión posterior. .

@funkybob De acuerdo, ciertamente la solución más compleja sería útil desde el lado del cliente, pero sería bueno tener esta funcionalidad incluida lo antes posible para que pueda comenzar a usarse antes de que se publique 1.0.

En realidad, al usar las relaciones públicas en una aplicación de producción, me gusta mucho la flexibilidad de la devolución de llamada proporcionada por # 538. Tengo un par de casos de uso en los que tengo que ocultar un recurso en tiempo de ejecución según el permiso.

Esto no sería posible para mí usando # 569

Hola alguna noticia Ese PR hace la vida más fácil, ¡gracias!

Ir con hack proporcionado por @dericcrago

+1

Una solución alternativa simple que estoy usando es anular los métodos get_detail & get_list para editar dichos campos.
Esto ahorra la sobrecarga de obtener los datos para el campo y luego eliminarlos del paquete, pero no estoy seguro de si este método es seguro para subprocesos, ya que parece que los objetos de recursos no se crean en cada llamada a la API.
Sería genial si alguien pudiera comentar sobre esto.

Aquí está el 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

otra solución es tener diferentes recursos para las vistas de lista y detalle:

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 la solución alternativa de @dnozay

+1 @dnozay , increíble

Tenga en cuenta que si desea que get_resource_uri funcione correctamente con la vista de detalles del usuario, deberá agregar lo siguiente después de definir UserResource .

UserResourceDetail.get_resource_uri = UserResource().get_resource_uri

De lo contrario, resource_uri estará vacío en todas las respuestas detalladas.

Posiblemente relacionado: # 1265

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

Roarster picture Roarster  ·  8Comentarios

adamzap picture adamzap  ·  18Comentarios

bastbnl picture bastbnl  ·  10Comentarios

lordi picture lordi  ·  6Comentarios

hashemian picture hashemian  ·  6Comentarios