Después de de esta extensa compilación de datos hemos podido resolver este enigma que presentan algunos de nuestros usuarios. Te regalamos la solución y deseamos serte de gran ayuda.
Solución:
Marco REST de Django 3.0+
Los campos dinámicos ahora son compatibles, consulte http://www.django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields — este enfoque define todos los campos en el serializador y luego le permite elimine selectivamente los que no desee.
O también podría hacer algo como esto para un Model Serializer, donde juega con Meta.fields en el inicio del serializador:
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('code',)
def __init__(self, *args, **kwargs):
if SHOW_CLASSIFICATION: # add logic here for optional viewing
self.Meta.fields = list(self.Meta.fields)
self.Meta.fields.append('classification')
super(ProductSerializer, self).__init__(*args, **kwargs)
Sin embargo, tendría que preguntarle a Tom si esta es la “manera correcta”, ya que puede no encajar con el plan a largo plazo.
Marco REST de Django <3.0
Prueba algo como esto:
class ProductSerializer(serializers.Serializer):
...
classification = serializers.SerializerMethodField('get_classification')
def get_classification(self, obj):
return getattr(obj, 'classification', None)
Múltiples serializadores
Otro enfoque sería crear múltiples serializadores con diferentes conjuntos de campos. Un serializador hereda de otro y agrega campos adicionales. Luego puede elegir el serializador apropiado en la vista con el get_serializer_class
método. Aquí hay un ejemplo real de cómo uso este enfoque para llamar a diferentes serializadores para presentar diferentes datos de usuario si el objeto del usuario es el mismo que el usuario de la solicitud.
def get_serializer_class(self):
""" An authenticated user looking at their own user object gets more data """
if self.get_object() == self.request.user:
return SelfUserSerializer
return UserSerializer
Quitar campos de la representación
Otro enfoque que he usado en contextos de seguridad es eliminar campos en el to_representation
método. Defina un método como
def remove_fields_from_representation(self, representation, remove_fields):
""" Removes fields from representation of instance. Call from
.to_representation() to apply field-level security.
* remove_fields: a list of fields to remove
"""
for remove_field in remove_fields:
try:
representation.pop(remove_field)
except KeyError:
# Ignore missing key -- a child serializer could inherit a "to_representation" method
# from its parent serializer that applies security to a field not present on
# the child serializer.
pass
y luego en su serializador, llame a ese método como
def to_representation(self, instance):
""" Apply field level security by removing fields for unauthorized users"""
representation = super(ProductSerializer, self).to_representation(instance)
if not permission_granted: # REPLACE WITH PERMISSION LOGIC
remove_fields = ('classification', )
self.remove_fields_from_representation(representation, remove_fields)
return representation
Este enfoque es sencillo y flexible, pero tiene el costo de serializar campos que a veces no se muestran. Pero probablemente esté bien.
Los serializadores están diseñados deliberadamente para usar un conjunto fijo de campos para que no pueda descartar fácilmente uno de los keys.
Podría usar un SerializerMethodField para devolver el valor del campo o None
si el campo no existe, o no podría usar serializadores y simplemente escribir una vista que devuelva la respuesta directamente.
Actualización para el marco REST 3.0serializer.fields
se puede modificar en un serializador instanciado. Cuando se requieren clases de serializador dinámico, probablemente sugiero modificar los campos de forma personalizada. Serializer.__init__()
método.
El método descrito a continuación hizo el trabajo por mí. Bastante simple, fácil y funcionó para mí.
Versión de DRF utilizada = djangorestframework (3.1.0)
class test(serializers.Serializer):
id= serializers.IntegerField()
name=serializers.CharField(required=False,default='some_default_value')