framework example ejemplo create django django-rest-framework

example - django rest framework post



¿Cómo puedo aplicar un filtro a un recurso anidado en el marco Django REST? (5)

En mi aplicación tengo los siguientes modelos:

class Zone(models.Model): name = models.SlugField() class ZonePermission(models.Model): zone = models.ForeignKey(''Zone'') user = models.ForeignKey(User) is_administrator = models.BooleanField() is_active = models.BooleanField()

Estoy usando el framework Django REST para crear un recurso que devuelva detalles de la zona más un recurso anidado que muestre los permisos del usuario autenticado para esa zona. La salida debería ser algo como esto:

{ "name": "test", "current_user_zone_permission": { "is_administrator": true, "is_active": true } }

Creé serializadores así:

class ZonePermissionSerializer(serializers.ModelSerializer): class Meta: model = ZonePermission fields = (''is_administrator'', ''is_active'') class ZoneSerializer(serializers.HyperlinkedModelSerializer): current_user_zone_permission = ZonePermissionSerializer(source=''zonepermission_set'') class Meta: model = Zone fields = (''name'', ''current_user_zone_permission'')

El problema con esto es que cuando solicito una zona en particular, el recurso anidado devuelve los registros de ZonePermission para todos los usuarios con permisos para esa zona. ¿Hay alguna forma de aplicar un filtro en request.user al recurso anidado?

Por cierto, no quiero usar un HyperlinkedIdentityField para esto (para minimizar las solicitudes http).

Solución

Esta es la solución que implementé en base a la respuesta a continuación. Agregué el siguiente código a mi clase de serializador:

current_user_zone_permission = serializers.SerializerMethodField(''get_user_zone_permission'') def get_user_zone_permission(self, obj): user = self.context[''request''].user zone_permission = ZonePermission.objects.get(zone=obj, user=user) serializer = ZonePermissionSerializer(zone_permission) return serializer.data

Muchas gracias por la solución!


Ahora puede crear una subclase de ListSerializer, utilizando el método que describí aquí: https://.com/a/28354281/3246023

Puede subclase el ListSerializer y sobrescribir el método to_representation.

Por defecto, el método to_representation llama a data.all () en el conjunto de consultas anidado. Entonces, efectivamente necesita hacer data = data.filter (** your_filters) antes de llamar al método. Luego, debe agregar su ListSerializer subclase como list_serializer_class en la meta del serializador anidado.

  1. subclase ListSerializer, sobrescribiendo a representación y luego llamando a super
  2. agregar ListSerializer subclasificado como meta list_serializer_class en el Serializador anidado

Debe usar filtro en lugar de obtener, de lo contrario, si se devuelve un registro múltiple, obtendrá Excepción.

current_user_zone_permission = serializers.SerializerMethodField(''get_user_zone_permission'') def get_user_zone_permission(self, obj): user = self.context[''request''].user zone_permission = ZonePermission.objects.filter(zone=obj, user=user) serializer = ZonePermissionSerializer(zone_permission,many=True) return serializer.data


Lo haría de una de dos maneras.

1) O hazlo a través de la captación previa en tu vista:

serializer = ZoneSerializer(Zone.objects.prefetch_related( Prefetch(''zone_permission_set'', queryset=ZonePermission.objects.filter(user=request.user), to_attr=''current_user_zone_permission'')) .get(id=pk))

2) O hazlo a través de la .to_representation:

class ZoneSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Zone fields = (''name'',) def to_representation(self, obj): data = super(ZoneSerializer, self).to_representation(obj) data[''current_user_zone_permission''] = ZonePermissionSerializer(ZonePermission.objects.filter(zone=obj, user=self.context[''request''].user)).data return data


Me enfrento con el mismo escenario. La mejor solución que he encontrado es usar un SerializerMethodField y tener esa consulta de método y devolver los valores deseados. Puede tener acceso a request.user en ese método a través de self.context[''request''].user .

Aún así, esto parece un poco complicado. Soy bastante nuevo en DRF, así que tal vez alguien con más experiencia pueda intervenir.


Si está usando el filtro QuerySet / en múltiples lugares, puede usar una función getter en su modelo , e incluso colocar el kwarg ''fuente'' para el serializador / campo. DRF llama automáticamente a funciones / callables si las encuentra al usar su función get_attribute .

class Zone(models.Model): name = models.SlugField() def current_user_zone_permission(self): return ZonePermission.objects.get(zone=self, user=user)

Me gusta este método porque mantiene su API constante bajo el capó con la API a través de HTTP.

class ZoneSerializer(serializers.HyperlinkedModelSerializer): current_user_zone_permission = ZonePermissionSerializer() class Meta: model = Zone fields = (''name'', ''current_user_zone_permission'')

¡Espero que esto ayude a algunas personas!

Nota: No es necesario que coincidan los nombres, puede usar el kwarg fuente si lo necesita / desea.

Editar: Me acabo de dar cuenta de que la función en el modelo no tiene acceso al usuario o la solicitud. Entonces quizás un campo de modelo personalizado / ListSerializer sería más adecuado para esta tarea.