10👍
You need to use another type of field:
class ClientSerializer(serializers.ModelSerializer):
email = serializers.EmailField(source='user.email')
def create(self, validated_data):
# DRF will create object {"user": {"email": "inputed_value"}} in validated_date
email = validated_data.get("user", {}).get('email')
class Meta:
model = Client
fields = (
"id",
"email",
)
20👍
Here is a read/write serializer method field:
class ReadWriteSerializerMethodField(serializers.SerializerMethodField):
def __init__(self, method_name=None, **kwargs):
self.method_name = method_name
kwargs['source'] = '*'
super(serializers.SerializerMethodField, self).__init__(**kwargs)
def to_internal_value(self, data):
return {self.field_name: data}
- [Django]-How to spread django unit tests over multiple files?
- [Django]-Django SUM Query?
- [Django]-How to server HTTP/2 Protocol with django
7👍
I tried to use Guilherme Nakayama da Silva and Julio Marins‘s answers to fix my problem with writing to a SerializerMethodField. It worked for reading and updating, but not for creating.
So I created my own WritableSerializerMethodField based on their answers, it works perfectly for reading, creating and writing.
class WritableSerializerMethodField(serializers.SerializerMethodField):
def __init__(self, **kwargs):
self.setter_method_name = kwargs.pop('setter_method_name', None)
self.deserializer_field = kwargs.pop('deserializer_field')
super().__init__(**kwargs)
self.read_only = False
def bind(self, field_name, parent):
retval = super().bind(field_name, parent)
if not self.setter_method_name:
self.setter_method_name = f'set_{field_name}'
return retval
def get_default(self):
default = super().get_default()
return {
self.field_name: default
}
def to_internal_value(self, data):
value = self.deserializer_field.to_internal_value(data)
method = getattr(self.parent, self.setter_method_name)
return {self.field_name: self.deserializer_field.to_internal_value(method(value))}
Then I used this in my serializer
class ProjectSerializer(serializers.ModelSerializer):
contract_price = WritableSerializerMethodField(deserializer_field=serializers.DecimalField(max_digits=12, decimal_places=2))
def get_contract_price(self, project):
return project.contract_price
def set_contract_price(self, value):
return value
- [Django]-Django: Does prefetch_related() follow reverse relationship lookup?
- [Django]-Threaded Django task doesn't automatically handle transactions or db connections?
- [Django]-Django.contrib.gis.db.backends.postgis vs django.db.backends.postgresql_psycopg2
4👍
In my case, I needed the logic inside my get_*
method and couldn’t fetch the value using the source
attribute. So I came up with this field.
class WritableSerializerMethodField(serializers.SerializerMethodField):
def __init__(self, method_name=None, **kwargs):
super().__init__(**kwargs)
self.read_only = False
def get_default(self):
default = super().get_default()
return {
self.field_name: default
}
def to_internal_value(self, data):
return {self.field_name: data}
- [Django]-Request.user returns a SimpleLazyObject, how do I "wake" it?
- [Django]-Django 1.7 – makemigrations not detecting changes
- [Django]-How to change status of JsonResponse in Django
1👍
You can override the save()
method on the serializer and use self.initial_data
. You’ll then need to do the validation on that field yourself though.
class MySerializer(serializers.ModelSerializer):
magic_field = serializers.SerializerMethodField()
def get_magic_field(self, instance):
return instance.get_magic_value()
def save(self, **kwargs):
super().save(**kwargs) # This creates/updates `self.instance`
if 'magic_field' in self.initial_data:
self.instance.update_magic_value(self.initial_data['magic_field'])
return self.instance
- [Django]-How to pass a variable from settings.py to a view?
- [Django]-Django REST Framework (DRF): Set current user id as field value
- [Django]-How to get the list of the authenticated users?
0👍
Why not just create the Client in the view instead?
def post(self, request, *args, **kwargs):
data = {
'email': request.data.get('email'),
}
serializer = ClientSerializer(data=data)
if serializer.is_valid():
email = serializer.data.get('email')
client = Client.objects.create(email=email)
# do other stuff
- [Django]-Django Rest Framework pagination extremely slow count
- [Django]-Django Passing data between views
- [Django]-Django 1.9 ImportError for import_module
0👍
I had the same issue and came up with the solution below.
Note that I really needed to use a SerializerMethodField
in my serializer, as I needed to populate a field based on request.user
and certain permissions, which was too complex for a SerializerField
, or other solutions proposed in other answers.
The solution was to “hijack” the perform_update
of the API View, and perform specific writes at that point (in my case, using another Serializer on top of the normal one). I only needed to do this with the update, but you may need to do it with perform_create
, if this is your use case.
It goes like this:
def perform_update(self, serializer):
if 'myField' in self.request.data and isinstance(self.request.data['myField'], bool):
if self.request.user == serializer.instance.owner:
serializer.instance.myField = self.request.data['myField']
else:
# we toggle myField in OtherClass
try:
other = models.OtherClass.objects.get(...)
except models. OtherClass.DoesNotExist:
return Response("You don't sufficient permissions to run this action.", status=status.HTTP_401_UNAUTHORIZED)
except models.OtherClass.MultipleObjectsReturned: # should never happen...
return Response("Internal Error: too many instances.", status=status.HTTP_500_INTERNAL_SERVER_ERROR)
else:
data = {
'myField': self.request.data['myField']
... # filled up with OtherClass params
}
otherSerializer = serializers.OtherClassSerializer(other, data=data)
if otherSerializer.is_valid():
otherSerializer.save()
serializer.save() # takes care of all the non-read-only fields
I have to admit that it is not ideal as per the MVC pattern, but it works.
- [Django]-How about having a SingletonModel in Django?
- [Django]-Annotating a Sum results in None rather than zero
- [Django]-How can I access environment variables directly in a Django template?
-4👍
You can repeat email
field, and it works, but it may make confused
class ClientSerializer(serializers.ModelSerializer):
email = serializers.SerializerMethodField()
email = serializers.EmailField(required=False)
def create(self, validated_data):
email = validated_data.get("email", None) # This doesn't work because email isn't passed into validated_data because it's a readonly field
# create the client and associated user here
def get_email(self, obj):
return obj.user.email
class Meta:
model = Client
fields = (
"id",
"email",
)
- [Django]-Django applying a style class based on a conditional
- [Django]-Django bulk_create function example
- [Django]-Python NameError: name 'include' is not defined