Reputation: 204
Using Django REST Framework (DRF), I am trying to follow the DRF documentation for nested serializers provided by this link. For the moment, let's assume that my code looks like the following:
models.py
class PvlEntry(models.Model):
pvl_project = models.OneToOneField("review.ProjectList", on_delete=models.CASCADE, related_name='pvl_project')
pvl_reviewer = models.ForeignKey('auth.User', on_delete=models.CASCADE, related_name='+')
pvl_worktype_is_correct = models.BooleanField(blank=False, null=False)
pvl_hw_description = models.TextField(blank=False, null=False)
class ProjectList(models.Model):
"""
"""
project_number = models.IntegerField(blank=False, null=False, unique=True)
project_manager = models.CharField(blank=False, max_length=255, null=False)
project_name = models.CharField(blank=False,
max_length=255,
null=False)
project_description = models.CharField(blank=True,
max_length=1024,
null=True)
views.py
class PvlEntryListCreateAPIView(ListCreateAPIView):
""" This view is leveraged for jsGrid so that we can have jsGrid produce
a JavaScript enabled view for actions like editing and filtering of
the project vetting list.
"""
queryset = PvlEntry.objects.all()
serializer_class = PvlEntrySerializer
name = 'listcreate-pvlentry'
def get_queryset(self):
qs = self.queryset.all()
return qs
class PvlEntryRetrieveUpdateDestroyAPIView(RetrieveUpdateDestroyAPIView):
""" Leveraged for jsGrid
"""
queryset = PvlEntry.objects.all()
serializer_class = PvlEntrySerializer
name = 'rud-pvlentry'
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
'id',
'first_name',
'last_name',
'email'
]
class ProjectListSerializer(serializers.ModelSerializer):
class Meta:
model = ProjectList
fields = '__all__'
class PvlEntrySerializer(serializers.ModelSerializer):
pvl_project = ProjectListSerializer()
pvl_reviewer = UserSerializer()
def update(self, instance, validated_data):
print(validated_data)
return super(PvlEntrySerializer, self).update(self, instance, validated_data)
class Meta:
model = PvlEntry
fields = '__all__'
Now, I understand that as this code sits now, it's not a writeable serializer. However, with the above code in place, should I not at least get past the serializer's validation of the data?
Using the DRF browsable API, when I attempt a PUT operation which is using the RetriveUpdateDestroy built-in API view, I get an error similar to the following:
PUT /pvl/grid/11
HTTP 400 Bad Request
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"pvl_project": {
"project_number": [
"pvl entry with this project number already exists."
]
}
}
Again, I understand that I can't perform an update (PUT) with the code as it is now, but shouldn't it at least pass the serialization/validation phase? I'm not performing the creation of a new record, either. I'm only attempting to perform an update. So, why should validation be concerned with whether or not a "pvl entry with this project number" already exists?
There are a lot of posts on stackoverflow that touch on or dance around this issue but I've not been able to lean on any of them for a resolution for some reason.
I have also tried going back and replacing the nested serializers with PrimaryKeyRelatedFields
but that approach doesn't return the related data, only references to the related data.
I have also experimented with separate serializers but that approach doesn't work well for the jsGrid JavaScript that I've implemented to consume the data in my template.
Surely there's a simple resolution?
Upvotes: 1
Views: 1528
Reputation: 752
First solution is that you can use PATCH
method instead of PUT
and don't send pvl_project
in form/ajax data (remember to set required attibute like pvl_project = ProjectListSerializer(required=False)
.
Problem is that in drf PUT
request method tries to replace all provided data in the given instance at one - which means it will firstly analyze that there is no PvlEntry.pvl_project
property duplicates because its OneToOneField
.
PATCH
on the other hand can update data partialy
(u can analyze this problem) and you don't even care about required=False
serializer attribute because it will only update data provided in request (how to update partial by PUT
method is answered in my last annotation).
Second
Now when we have first concept resolved we can move on and make this serializer to work. Lets' assume that you have uuid attribute set on every model (using id
field is not the best practice) and you want to set pvl_project
by the given uuid
attribute value.
You can override to_internal_value
method in ProjectListSerializer
which is used on saving instance and simply search for the given object by the given data.
class ProjectListSerializer(serializers.ModelSerializer):
class Meta:
model = ProjectList
fields = '__all__'
def to_internal_value(self, data):
# you can validate data here for sure that provided data is correct uuid string
try:
return ProjectList.objects.get(uuid=data)
except ProjectList.DoesNotExist:
raise ValidationError(['Project list with the given uuid does not exist'])
This serializer works now like a good elastic switch between json <-> api <-> django model instance world.
Upvotes: 1