Reputation: 2409
I have a model representing a Status
- and have a foreign key to Status
from an Object
model. I want to be able to create new objects, but do not want to allow the possibility of creating any more Status
entries (there are a set of 5 pre-defined ones that are migrated into the database). I believe I have figured out how to structure serializers in such a way as to only re-use existing Status
entries, but I'm not sure if it is the best way to go about doing something like this...
Some Simplified Code:
class StatusSerializer(serializers.ModelSerializer):
class Meta:
model = Status
fields = ('name',)
def to_representation(self, obj):
return obj.name
def to_internal_value(self, data):
return {
'name': data
}
class ObjectSerializer(serializers.ModelSerializer):
status = StatusSerializer(read_only=True)
class Meta:
model = Object
fields = ('obj_name', 'status',)
def create(self, validated_data):
# We do not want to create new statuses - only use existing ones
status = Status.objects.get(name=self.initial_data['status'])
return Object.objects.create(status=status, **validated_data)
def update(self, instance, validated_data):
instance.obj_name = validated_data.get('obj_name', instance.obj_name)
# We do not want to create new statuses - only use existing ones
instance.status = Status.objects.get(name=self.initial_data['status']) if 'status' in self.initial_data else instance.status
return instance
As seen above, I also flatten out the Status
object when it is displayed - e.g. I turn the following
{
'obj_name': 'ObjectName',
'status': {
'name': 'StatusName'
}
}
Into this
{
'obj_name': 'ObjectName',
'status': 'StatusName'
}
This seems to work fine -- however I am not sure how to handle cases where a user of the API gives me an invalid Status
name. Right now, the api would bubble up a Status.DoesNotExist
exception from one of the Status.objects.get(...)
requests - should I just catch that and re-raise as something the serializer/view would expect?
Thanks!
Edit: Realized my question wasnt exactly clear...
Status
objects - and enforce that any Object
created will use one of those statuses?Object
with an invalid Status
name?Upvotes: 1
Views: 60
Reputation: 821
You can use validate
method in the ObjectSerializer
.
class ObjectSerializer(serializers.ModelSerializer):
status = StatusSerializer(read_only=True)
class Meta:
model = Object
fields = ('obj_name', 'status',)
def validate(self, attrs):
validated_data = super().validate(attrs)
status = self.initial_data.get('status')
# Here assuming that None is not the valid value for status
if status is not None:
status_obj = Status.objects.filter(name=status)
if not status_obj:
raise serializer.ValidationError('Invalid Status')
status_obj = status_obj[0]
validated_data['status'] = status_obj
return validated_data
# Nothing special to be done in create/update since we are sending data
# in validated_data which will directly sent to the instance.
# def create(self, validated_data):
# We do not want to create new statuses - only use existing ones
# status = Status.objects.get(name=self.initial_data['status'])
#return Object.objects.create(status=status, **validated_data)
#def update(self, instance, validated_data):
# instance.obj_name = validated_data.get('obj_name', instance.obj_name)
# We do not want to create new statuses - only use existing ones
# instance.status = Status.objects.get(name=self.initial_data['status']) if 'status' in self.initial_data else instance.status
# return instance
And if you can use different keys for write and read status
, then you can modify ObjectSerializer
like this:
class ObjectSerializer(serializer.ModelSerializer):
status = serializer.SlugRelatedField(slug_field='name', queryset=Status.objects.all(), write_only=True)
status_data = StatusSerializer(read_only=True, source='status')
class Meta:
model = Object
fields = ('obj_name', 'status', 'status_data')
In this case, if you pass {'obj_name': 'ObjectName', 'status': 'StatusName'}
data to serializer, the serializer will first check for the StatusName
value in the name
field of the provided queryset (In this case we are using all
) and if not valid raises ValidationError
. If valid, then saves the status
in the field of the instance.
Upvotes: 1