with framework example create django django-rest-framework

framework - Permiso por campo en el marco REST de Django



django rest framework serializer (6)

¿Qué hay de cambiar la clase serializador basado en el usuario?

En la documentación:

http://www.django-rest-framework.org/api-guide/generic-views/#get_serializer_classself

def get_serializer_class(self): if self.request.user.is_staff: return FullAccountSerializer return BasicAccountSerializer

Estoy usando Django REST Framework para serializar un modelo de Django. Tengo una vista ListCreateAPIView para enumerar los objetos y una vista RetrieveUpdateDestroyAPIView para recuperar / actualizar / eliminar objetos individuales. El modelo almacena información que los usuarios envían ellos mismos. La información que envían contiene información privada y información pública. Quiero que todos los usuarios puedan enumerar y recuperar la información pública, pero solo quiero que el propietario enumere / recupere / actualice / elimine la información privada. Por lo tanto, necesito permisos por campo y no permisos de objeto.

La sugerencia más cercana que encontré fue https://groups.google.com/forum/#!topic/django-rest-framework/FUd27n_k3U0 que cambia el serializador según el tipo de solicitud. Esto no funcionará para mi situación porque no tengo el queryset u objeto en ese punto para determinar si es propiedad del usuario o no.

Por supuesto, tengo mi interfaz ocultando la información privada, pero las personas inteligentes todavía pueden rastrear las solicitudes de API para obtener los objetos completos. Si es necesario el código, puedo proporcionarlo, pero mi solicitud se aplica a los diseños de VT DANGO Framework de vainilla.


Aquí:

- models.py:

class Article(models.Model): name = models.CharField(max_length=50, blank=False) author = models.CharField(max_length=50, blank=True) def __str__(self): return u"%s" % self.name class Meta: permissions = ( # name (''read_name_article'', "Read article''s name"), (''change_name_article'', "Change article''s name"), # author (''read_author_article'', "Read article''s author"), (''change_author_article'', "Change article''s author"), )

- serializers.py:

class ArticleSerializer(serializers.ModelSerializer): class Meta(object): model = Article fields = "__all__" def to_representation(self, request_data): # get the original representation ret = super(ArticleSerializer, self).to_representation(request_data) current_user = self.context[''request''].user for field_name, field_value in sorted(ret.items()): if not current_user.has_perm( ''app_name.read_{}_article''.format(field_name) ): ret.pop(field_name) # remove field if it''s not permitted return ret def to_internal_value(self, request_data): errors = {} # get the original representation ret = super(ArticleSerializer, self).to_internal_value(request_data) current_user = self.context[''request''].user for field_name, field_value in sorted(ret.items()): if field_value and not current_user.has_perm( ''app_name.change_{}_article''.format(field_name) ): errors[field_name] = ["Field not allowed to change"] # throw error if it''s not permitted if errors: raise ValidationError(errors) return ret


Descubrí una manera de hacerlo. En el serializador, tengo acceso tanto al objeto como al usuario que realiza la solicitud de API. Por lo tanto, puedo verificar si el solicitante es el propietario del objeto y devolver la información privada. Si no lo son, el serializador devolverá una cadena vacía.

class UserInfoSerializer(serializers.HyperlinkedModelSerializer): private_field1 = serializers.SerializerMethodField(''get_private_field1'') class Meta: model = UserInfo fields = ( ''id'', ''public_field1'', ''public_field2'', ''private_field1'', ) read_only_fields = (''id'') def get_private_field1(self, obj): # obj.created_by is the foreign key to the user model if obj.created_by != self.context[''request''].user: return "" else: return obj.private_field1


En caso de que solo esté realizando operaciones de LECTURA, simplemente puede abrir los campos en el método de representación del serializador.

def to_representation(self,instance): ret = super(YourSerializer,self).to_representation(instance) fields_to_pop = [''field1'',''field2'',''field3''] if instance.created_by != self.context[''request''].user.id: [ret.pop(field,'''') for field in fields_to_pop] return ret

Esto debería ser suficiente para ocultar campos sensibles.


Para una solución que permita leer y escribir, haga esto:

class PrivateField(serializers.Field): def get_attribute(self, obj): # We pass the object instance onto `to_representation`, # not just the field attribute. return obj def to_representation(self, obj): # for read functionality if obj.created_by != self.context[''request''].user: return "" else: return obj.private_field1 def to_internal_value(self, data): # for write functionality # check if data is valid and if not raise ValidationError class UserInfoSerializer(serializers.HyperlinkedModelSerializer): private_field1 = PrivateField() ...

Ver la docs para un ejemplo.


Tuve un problema similar el otro día. Aquí está mi enfoque:

Esta es una solución DRF 2.4 .

class PrivateField(serializers.Field): def field_to_native(self, obj, field_name): """ Return null value if request has no access to that field """ if obj.created_by == self.context.get(''request'').user: return super(PrivateField, self).field_to_native(obj, field_name) return None #Usage class UserInfoSerializer(serializers.ModelSerializer): private_field1 = PrivateField() private_field2 = PrivateField() class Meta: model = UserInfo

Y una solución DRF 3.x:

class PrivateField(serializers.ReadOnlyField): def get_attribute(self, instance): """ Given the *outgoing* object instance, return the primitive value that should be used for this field. """ if instance.created_by == self.context[''request''].user: return super(PrivateField, self).get_attribute(instance) return None

Esta vez extendemos ReadOnlyField solo porque to_representation no está implementado en la clase serializers.Field .