Django Restframework: Writable nested serializer with custom behavior The 2019 Stack Overflow Developer Survey Results Are InDjango Rest Framework - add/remove to a listDjango Custom Decorator for user group checkDjango Model field naming convention for lookup tablesDjango REST custom methods for generic viewsStoring database references to files/mediaDjango Notification Model with a generic relationWorking with multiple currencies in DjangoDjango Rest Framework Totaling API values in different waysCustom login class based viewPython graphene-graphql, are global constants for SQL queries a good idea?
Slides for 30 min~1 hr Skype tenure track application interview
Mathematics of imaging the black hole
APIPA and LAN Broadcast Domain
What is the most efficient way to store a numeric range?
Is it ethical to upload a automatically generated paper to a non peer-reviewed site as part of a larger research?
adding a scale to my ternary condition division result?
What is the meaning of Triage in Cybersec world?
Is it okay to consider publishing in my first year of PhD?
When should I buy a clipper card after flying to Oakland?
How can I add encounters in the Lost Mine of Phandelver campaign without giving PCs too much XP?
Why couldn't they take pictures of a closer black hole?
How to deal with speedster characters?
What to expect from an e-bike service?
Can we generate random numbers using irrational numbers like π and e?
For what reasons would an animal species NOT cross a *horizontal* land bridge?
Sums of normal random variables
Match Roman Numerals
How to support a colleague who finds meetings extremely tiring?
How can I define good in a religion that claims no moral authority?
Why not take a picture of a closer black hole?
What is the motivation for a law requiring 2 parties to consent for recording a conversation
Landlord wants to switch my lease to a "Land contract" to "get back at the city"
Can there be female White Walkers?
Time travel alters history but people keep saying nothing's changed
Django Restframework: Writable nested serializer with custom behavior
The 2019 Stack Overflow Developer Survey Results Are InDjango Rest Framework - add/remove to a listDjango Custom Decorator for user group checkDjango Model field naming convention for lookup tablesDjango REST custom methods for generic viewsStoring database references to files/mediaDjango Notification Model with a generic relationWorking with multiple currencies in DjangoDjango Rest Framework Totaling API values in different waysCustom login class based viewPython graphene-graphql, are global constants for SQL queries a good idea?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
$begingroup$
Context
I am trying to get some practice with Django and Django REST framework.
As a toy example I want to set up a server to which I can POST results of automated test runs.
In the simplest version, a test result contains the following information:
- a testcase id, which is unique and will never change
- a testcase name, which might change from time to time
- a verdict, which may only take one of the predefined values "PASS", "FAIL" or "SKIPPED"
I came up with three different possible solutions. Each one is working, but I am not sure which one is the best approach since I am lacking experience.
Expected behavior
The implementation should fulfill the following requirements:
- POST request containing any other verdict than PASS, FAIL or SKIPPED -> HTTP 400
- POST request with a new and yet unused testcase id -> create and use new testcase instance
- POST request with an existing testcase id but different testcase name-> retrieve existing testcase instance for this id and update the name in the database
- GET request -> return list of all test results with testcase id, testcase name and verdict
Django Models
For the models I have the following code:
models.py:
from django.db import models
class Testcase(models.Model):
uid = models.CharField(max_length=10, unique=True)
name = models.CharField(max_length=50)
def __str__(self):
return f"self.uid:<10 self.name:<50"
class Verdict(models.Model):
value = models.CharField(max_length=10, unique=True)
def __str__(self):
return f"self.value:>10"
class Testresult(models.Model):
testcase = models.ForeignKey(Testcase, related_name='results', on_delete=models.CASCADE) # many-to-one
verdict = models.ForeignKey(Verdict, on_delete=models.CASCADE) # many-to-one
def __str__(self):
return f"self.testcase: self.verdict"
Take One: use base ModelSerializers and let the ViewSet handle create()
views.py:
from rest_framework import viewsets, status
from rest_framework.response import Response
from .models import Testresult, Testcase, Verdict
from .serializers import StandardTestresultSerializer
class CustomTestresultViewSet(viewsets.ModelViewSet):
queryset = Testresult.objects.all()
serializer_class = StandardTestresultSerializer
def create(self, request, *args, **kwargs):
try:
verdict = Verdict.objects.get(value=request.data["verdict.value"])
except (Verdict.DoesNotExist, KeyError):
return Response(status=status.HTTP_400_BAD_REQUEST)
testcase = self._create_or_update_testcase(request)
testresult = Testresult.objects.create(verdict=verdict, testcase=testcase)
return Response(StandardTestresultSerializer(testresult).data)
def _create_or_update_testcase(self, request):
uid = request.data.get("testcase.uid")
name = request.data.get("testcase.name")
try:
testcase = Testcase.objects.get(uid=uid)
if testcase.name != name:
testcase.name = name
testcase.save()
except Testcase.DoesNotExist:
testcase = Testcase.objects.create(**request.data["testcase"])
return testcase
serializers.py:
from rest_framework import serializers
from .models import Testresult, Verdict, Testcase
class StandardVerdictSerializer(serializers.ModelSerializer):
class Meta:
model = Verdict
fields = ('value',)
class StandardTestcaseSerializer(serializers.ModelSerializer):
class Meta:
model = Testcase
fields = ('uid', 'name')
class StandardTestresultSerializer(serializers.ModelSerializer):
testcase = StandardTestcaseSerializer()
verdict = StandardVerdictSerializer()
class Meta:
model = Testresult
fields = ('testcase', 'verdict')
What I like about this approach
The logic for looking up / creating / updating Verdict and Testcase instances and returning error statiuses lives in the View class.
From what I've understood so far, serializers are not really intended to use as a way to retrieve already existing instances.
What I don't like
Input validation which for Verdict and Testcase is bypassed, what would normally be done by the serializers. This is currently not too much of a problem, but if (for example) the Verdict model gets a new field date this could become more important.
Take Two: Using a simple View class and more complex Serializers classes
views.py:
class StandardTestresultViewSet(viewsets.ModelViewSet):
queryset = Testresult.objects.all()
serializer_class = UnvalidatedTestresultSerializer
serializers.py:
class UnvalidatedVerdictSerializer(serializers.ModelSerializer):
class Meta:
model = Verdict
fields = ('value',)
extra_kwargs =
'value': 'validators': []
def create(self, validated_data):
try:
return Verdict.objects.get(**validated_data)
except ObjectDoesNotExist:
raise serializers.ValidationError(detail="Unknown result value", code=status.HTTP_400_BAD_REQUEST)
class UnvalidatedTestcaseSerializer(serializers.ModelSerializer):
class Meta:
model = Testcase
fields = ('uid', 'name')
extra_kwargs =
'uid': 'validators': []
def create(self, validated_data):
uid = validated_data.get('uid')
name = validated_data.get('name')
try:
testcase = Testcase.objects.get(uid=uid)
if testcase.name != name:
testcase.name = name
testcase.save()
except ObjectDoesNotExist:
testcase = Testcase.objects.create(**validated_data)
return testcase
class UnvalidatedTestresultSerializer(serializers.ModelSerializer):
testcase = UnvalidatedTestcaseSerializer()
verdict = UnvalidatedVerdictSerializer()
class Meta:
model = Testresult
fields = ('testcase', 'verdict')
def create(self, validated_data):
verdict_data = validated_data.pop('verdict')
serialized_verdict = UnvalidatedVerdictSerializer(data=verdict_data)
serialized_verdict.is_valid(raise_exception=True)
verdict_instance = serialized_verdict.save()
testcase_data = validated_data.pop('testcase')
serialized_testcase = UnvalidatedTestcaseSerializer(data=testcase_data)
serialized_testcase.is_valid(raise_exception=True)
testcase_instance = serialized_testcase.save()
return Testresult.objects.create(testcase=testcase_instance, verdict=verdict_instance)
What I like
Validation mechanisms of ModelSerializer can be utilized.
What I don't like
create methods don't actually create Model instances but merely retrieve and udpate them if necessary.
GET requests will also be using the "Unvalidated" serializers, which I don't see as a problem right now, but maybe that is also not a good practice?
Take Three: combining both approaches and select the serializer class to use depending on the request method
In this approach the same serializer classes as shown in Take One and Take Two will be used, so I'm only giving the code for the View class.
views.py:
class DynamicTestresultViewSet(viewsets.ModelViewSet):
queryset = Testresult.objects.all()
def get_serializer_class(self):
if self.request.method == "POST":
return UnvalidatedTestresultSerializer
else:
return StandardTestresultSerializer
What I like
Serializers are used to parse and validate the input data of the request. GET requests will be served using the "normal" serializer.
What I don't like
Requires six serializer classes instead of three.
What approach would you suggest, or would you do it entirely different?
python django
$endgroup$
add a comment |
$begingroup$
Context
I am trying to get some practice with Django and Django REST framework.
As a toy example I want to set up a server to which I can POST results of automated test runs.
In the simplest version, a test result contains the following information:
- a testcase id, which is unique and will never change
- a testcase name, which might change from time to time
- a verdict, which may only take one of the predefined values "PASS", "FAIL" or "SKIPPED"
I came up with three different possible solutions. Each one is working, but I am not sure which one is the best approach since I am lacking experience.
Expected behavior
The implementation should fulfill the following requirements:
- POST request containing any other verdict than PASS, FAIL or SKIPPED -> HTTP 400
- POST request with a new and yet unused testcase id -> create and use new testcase instance
- POST request with an existing testcase id but different testcase name-> retrieve existing testcase instance for this id and update the name in the database
- GET request -> return list of all test results with testcase id, testcase name and verdict
Django Models
For the models I have the following code:
models.py:
from django.db import models
class Testcase(models.Model):
uid = models.CharField(max_length=10, unique=True)
name = models.CharField(max_length=50)
def __str__(self):
return f"self.uid:<10 self.name:<50"
class Verdict(models.Model):
value = models.CharField(max_length=10, unique=True)
def __str__(self):
return f"self.value:>10"
class Testresult(models.Model):
testcase = models.ForeignKey(Testcase, related_name='results', on_delete=models.CASCADE) # many-to-one
verdict = models.ForeignKey(Verdict, on_delete=models.CASCADE) # many-to-one
def __str__(self):
return f"self.testcase: self.verdict"
Take One: use base ModelSerializers and let the ViewSet handle create()
views.py:
from rest_framework import viewsets, status
from rest_framework.response import Response
from .models import Testresult, Testcase, Verdict
from .serializers import StandardTestresultSerializer
class CustomTestresultViewSet(viewsets.ModelViewSet):
queryset = Testresult.objects.all()
serializer_class = StandardTestresultSerializer
def create(self, request, *args, **kwargs):
try:
verdict = Verdict.objects.get(value=request.data["verdict.value"])
except (Verdict.DoesNotExist, KeyError):
return Response(status=status.HTTP_400_BAD_REQUEST)
testcase = self._create_or_update_testcase(request)
testresult = Testresult.objects.create(verdict=verdict, testcase=testcase)
return Response(StandardTestresultSerializer(testresult).data)
def _create_or_update_testcase(self, request):
uid = request.data.get("testcase.uid")
name = request.data.get("testcase.name")
try:
testcase = Testcase.objects.get(uid=uid)
if testcase.name != name:
testcase.name = name
testcase.save()
except Testcase.DoesNotExist:
testcase = Testcase.objects.create(**request.data["testcase"])
return testcase
serializers.py:
from rest_framework import serializers
from .models import Testresult, Verdict, Testcase
class StandardVerdictSerializer(serializers.ModelSerializer):
class Meta:
model = Verdict
fields = ('value',)
class StandardTestcaseSerializer(serializers.ModelSerializer):
class Meta:
model = Testcase
fields = ('uid', 'name')
class StandardTestresultSerializer(serializers.ModelSerializer):
testcase = StandardTestcaseSerializer()
verdict = StandardVerdictSerializer()
class Meta:
model = Testresult
fields = ('testcase', 'verdict')
What I like about this approach
The logic for looking up / creating / updating Verdict and Testcase instances and returning error statiuses lives in the View class.
From what I've understood so far, serializers are not really intended to use as a way to retrieve already existing instances.
What I don't like
Input validation which for Verdict and Testcase is bypassed, what would normally be done by the serializers. This is currently not too much of a problem, but if (for example) the Verdict model gets a new field date this could become more important.
Take Two: Using a simple View class and more complex Serializers classes
views.py:
class StandardTestresultViewSet(viewsets.ModelViewSet):
queryset = Testresult.objects.all()
serializer_class = UnvalidatedTestresultSerializer
serializers.py:
class UnvalidatedVerdictSerializer(serializers.ModelSerializer):
class Meta:
model = Verdict
fields = ('value',)
extra_kwargs =
'value': 'validators': []
def create(self, validated_data):
try:
return Verdict.objects.get(**validated_data)
except ObjectDoesNotExist:
raise serializers.ValidationError(detail="Unknown result value", code=status.HTTP_400_BAD_REQUEST)
class UnvalidatedTestcaseSerializer(serializers.ModelSerializer):
class Meta:
model = Testcase
fields = ('uid', 'name')
extra_kwargs =
'uid': 'validators': []
def create(self, validated_data):
uid = validated_data.get('uid')
name = validated_data.get('name')
try:
testcase = Testcase.objects.get(uid=uid)
if testcase.name != name:
testcase.name = name
testcase.save()
except ObjectDoesNotExist:
testcase = Testcase.objects.create(**validated_data)
return testcase
class UnvalidatedTestresultSerializer(serializers.ModelSerializer):
testcase = UnvalidatedTestcaseSerializer()
verdict = UnvalidatedVerdictSerializer()
class Meta:
model = Testresult
fields = ('testcase', 'verdict')
def create(self, validated_data):
verdict_data = validated_data.pop('verdict')
serialized_verdict = UnvalidatedVerdictSerializer(data=verdict_data)
serialized_verdict.is_valid(raise_exception=True)
verdict_instance = serialized_verdict.save()
testcase_data = validated_data.pop('testcase')
serialized_testcase = UnvalidatedTestcaseSerializer(data=testcase_data)
serialized_testcase.is_valid(raise_exception=True)
testcase_instance = serialized_testcase.save()
return Testresult.objects.create(testcase=testcase_instance, verdict=verdict_instance)
What I like
Validation mechanisms of ModelSerializer can be utilized.
What I don't like
create methods don't actually create Model instances but merely retrieve and udpate them if necessary.
GET requests will also be using the "Unvalidated" serializers, which I don't see as a problem right now, but maybe that is also not a good practice?
Take Three: combining both approaches and select the serializer class to use depending on the request method
In this approach the same serializer classes as shown in Take One and Take Two will be used, so I'm only giving the code for the View class.
views.py:
class DynamicTestresultViewSet(viewsets.ModelViewSet):
queryset = Testresult.objects.all()
def get_serializer_class(self):
if self.request.method == "POST":
return UnvalidatedTestresultSerializer
else:
return StandardTestresultSerializer
What I like
Serializers are used to parse and validate the input data of the request. GET requests will be served using the "normal" serializer.
What I don't like
Requires six serializer classes instead of three.
What approach would you suggest, or would you do it entirely different?
python django
$endgroup$
add a comment |
$begingroup$
Context
I am trying to get some practice with Django and Django REST framework.
As a toy example I want to set up a server to which I can POST results of automated test runs.
In the simplest version, a test result contains the following information:
- a testcase id, which is unique and will never change
- a testcase name, which might change from time to time
- a verdict, which may only take one of the predefined values "PASS", "FAIL" or "SKIPPED"
I came up with three different possible solutions. Each one is working, but I am not sure which one is the best approach since I am lacking experience.
Expected behavior
The implementation should fulfill the following requirements:
- POST request containing any other verdict than PASS, FAIL or SKIPPED -> HTTP 400
- POST request with a new and yet unused testcase id -> create and use new testcase instance
- POST request with an existing testcase id but different testcase name-> retrieve existing testcase instance for this id and update the name in the database
- GET request -> return list of all test results with testcase id, testcase name and verdict
Django Models
For the models I have the following code:
models.py:
from django.db import models
class Testcase(models.Model):
uid = models.CharField(max_length=10, unique=True)
name = models.CharField(max_length=50)
def __str__(self):
return f"self.uid:<10 self.name:<50"
class Verdict(models.Model):
value = models.CharField(max_length=10, unique=True)
def __str__(self):
return f"self.value:>10"
class Testresult(models.Model):
testcase = models.ForeignKey(Testcase, related_name='results', on_delete=models.CASCADE) # many-to-one
verdict = models.ForeignKey(Verdict, on_delete=models.CASCADE) # many-to-one
def __str__(self):
return f"self.testcase: self.verdict"
Take One: use base ModelSerializers and let the ViewSet handle create()
views.py:
from rest_framework import viewsets, status
from rest_framework.response import Response
from .models import Testresult, Testcase, Verdict
from .serializers import StandardTestresultSerializer
class CustomTestresultViewSet(viewsets.ModelViewSet):
queryset = Testresult.objects.all()
serializer_class = StandardTestresultSerializer
def create(self, request, *args, **kwargs):
try:
verdict = Verdict.objects.get(value=request.data["verdict.value"])
except (Verdict.DoesNotExist, KeyError):
return Response(status=status.HTTP_400_BAD_REQUEST)
testcase = self._create_or_update_testcase(request)
testresult = Testresult.objects.create(verdict=verdict, testcase=testcase)
return Response(StandardTestresultSerializer(testresult).data)
def _create_or_update_testcase(self, request):
uid = request.data.get("testcase.uid")
name = request.data.get("testcase.name")
try:
testcase = Testcase.objects.get(uid=uid)
if testcase.name != name:
testcase.name = name
testcase.save()
except Testcase.DoesNotExist:
testcase = Testcase.objects.create(**request.data["testcase"])
return testcase
serializers.py:
from rest_framework import serializers
from .models import Testresult, Verdict, Testcase
class StandardVerdictSerializer(serializers.ModelSerializer):
class Meta:
model = Verdict
fields = ('value',)
class StandardTestcaseSerializer(serializers.ModelSerializer):
class Meta:
model = Testcase
fields = ('uid', 'name')
class StandardTestresultSerializer(serializers.ModelSerializer):
testcase = StandardTestcaseSerializer()
verdict = StandardVerdictSerializer()
class Meta:
model = Testresult
fields = ('testcase', 'verdict')
What I like about this approach
The logic for looking up / creating / updating Verdict and Testcase instances and returning error statiuses lives in the View class.
From what I've understood so far, serializers are not really intended to use as a way to retrieve already existing instances.
What I don't like
Input validation which for Verdict and Testcase is bypassed, what would normally be done by the serializers. This is currently not too much of a problem, but if (for example) the Verdict model gets a new field date this could become more important.
Take Two: Using a simple View class and more complex Serializers classes
views.py:
class StandardTestresultViewSet(viewsets.ModelViewSet):
queryset = Testresult.objects.all()
serializer_class = UnvalidatedTestresultSerializer
serializers.py:
class UnvalidatedVerdictSerializer(serializers.ModelSerializer):
class Meta:
model = Verdict
fields = ('value',)
extra_kwargs =
'value': 'validators': []
def create(self, validated_data):
try:
return Verdict.objects.get(**validated_data)
except ObjectDoesNotExist:
raise serializers.ValidationError(detail="Unknown result value", code=status.HTTP_400_BAD_REQUEST)
class UnvalidatedTestcaseSerializer(serializers.ModelSerializer):
class Meta:
model = Testcase
fields = ('uid', 'name')
extra_kwargs =
'uid': 'validators': []
def create(self, validated_data):
uid = validated_data.get('uid')
name = validated_data.get('name')
try:
testcase = Testcase.objects.get(uid=uid)
if testcase.name != name:
testcase.name = name
testcase.save()
except ObjectDoesNotExist:
testcase = Testcase.objects.create(**validated_data)
return testcase
class UnvalidatedTestresultSerializer(serializers.ModelSerializer):
testcase = UnvalidatedTestcaseSerializer()
verdict = UnvalidatedVerdictSerializer()
class Meta:
model = Testresult
fields = ('testcase', 'verdict')
def create(self, validated_data):
verdict_data = validated_data.pop('verdict')
serialized_verdict = UnvalidatedVerdictSerializer(data=verdict_data)
serialized_verdict.is_valid(raise_exception=True)
verdict_instance = serialized_verdict.save()
testcase_data = validated_data.pop('testcase')
serialized_testcase = UnvalidatedTestcaseSerializer(data=testcase_data)
serialized_testcase.is_valid(raise_exception=True)
testcase_instance = serialized_testcase.save()
return Testresult.objects.create(testcase=testcase_instance, verdict=verdict_instance)
What I like
Validation mechanisms of ModelSerializer can be utilized.
What I don't like
create methods don't actually create Model instances but merely retrieve and udpate them if necessary.
GET requests will also be using the "Unvalidated" serializers, which I don't see as a problem right now, but maybe that is also not a good practice?
Take Three: combining both approaches and select the serializer class to use depending on the request method
In this approach the same serializer classes as shown in Take One and Take Two will be used, so I'm only giving the code for the View class.
views.py:
class DynamicTestresultViewSet(viewsets.ModelViewSet):
queryset = Testresult.objects.all()
def get_serializer_class(self):
if self.request.method == "POST":
return UnvalidatedTestresultSerializer
else:
return StandardTestresultSerializer
What I like
Serializers are used to parse and validate the input data of the request. GET requests will be served using the "normal" serializer.
What I don't like
Requires six serializer classes instead of three.
What approach would you suggest, or would you do it entirely different?
python django
$endgroup$
Context
I am trying to get some practice with Django and Django REST framework.
As a toy example I want to set up a server to which I can POST results of automated test runs.
In the simplest version, a test result contains the following information:
- a testcase id, which is unique and will never change
- a testcase name, which might change from time to time
- a verdict, which may only take one of the predefined values "PASS", "FAIL" or "SKIPPED"
I came up with three different possible solutions. Each one is working, but I am not sure which one is the best approach since I am lacking experience.
Expected behavior
The implementation should fulfill the following requirements:
- POST request containing any other verdict than PASS, FAIL or SKIPPED -> HTTP 400
- POST request with a new and yet unused testcase id -> create and use new testcase instance
- POST request with an existing testcase id but different testcase name-> retrieve existing testcase instance for this id and update the name in the database
- GET request -> return list of all test results with testcase id, testcase name and verdict
Django Models
For the models I have the following code:
models.py:
from django.db import models
class Testcase(models.Model):
uid = models.CharField(max_length=10, unique=True)
name = models.CharField(max_length=50)
def __str__(self):
return f"self.uid:<10 self.name:<50"
class Verdict(models.Model):
value = models.CharField(max_length=10, unique=True)
def __str__(self):
return f"self.value:>10"
class Testresult(models.Model):
testcase = models.ForeignKey(Testcase, related_name='results', on_delete=models.CASCADE) # many-to-one
verdict = models.ForeignKey(Verdict, on_delete=models.CASCADE) # many-to-one
def __str__(self):
return f"self.testcase: self.verdict"
Take One: use base ModelSerializers and let the ViewSet handle create()
views.py:
from rest_framework import viewsets, status
from rest_framework.response import Response
from .models import Testresult, Testcase, Verdict
from .serializers import StandardTestresultSerializer
class CustomTestresultViewSet(viewsets.ModelViewSet):
queryset = Testresult.objects.all()
serializer_class = StandardTestresultSerializer
def create(self, request, *args, **kwargs):
try:
verdict = Verdict.objects.get(value=request.data["verdict.value"])
except (Verdict.DoesNotExist, KeyError):
return Response(status=status.HTTP_400_BAD_REQUEST)
testcase = self._create_or_update_testcase(request)
testresult = Testresult.objects.create(verdict=verdict, testcase=testcase)
return Response(StandardTestresultSerializer(testresult).data)
def _create_or_update_testcase(self, request):
uid = request.data.get("testcase.uid")
name = request.data.get("testcase.name")
try:
testcase = Testcase.objects.get(uid=uid)
if testcase.name != name:
testcase.name = name
testcase.save()
except Testcase.DoesNotExist:
testcase = Testcase.objects.create(**request.data["testcase"])
return testcase
serializers.py:
from rest_framework import serializers
from .models import Testresult, Verdict, Testcase
class StandardVerdictSerializer(serializers.ModelSerializer):
class Meta:
model = Verdict
fields = ('value',)
class StandardTestcaseSerializer(serializers.ModelSerializer):
class Meta:
model = Testcase
fields = ('uid', 'name')
class StandardTestresultSerializer(serializers.ModelSerializer):
testcase = StandardTestcaseSerializer()
verdict = StandardVerdictSerializer()
class Meta:
model = Testresult
fields = ('testcase', 'verdict')
What I like about this approach
The logic for looking up / creating / updating Verdict and Testcase instances and returning error statiuses lives in the View class.
From what I've understood so far, serializers are not really intended to use as a way to retrieve already existing instances.
What I don't like
Input validation which for Verdict and Testcase is bypassed, what would normally be done by the serializers. This is currently not too much of a problem, but if (for example) the Verdict model gets a new field date this could become more important.
Take Two: Using a simple View class and more complex Serializers classes
views.py:
class StandardTestresultViewSet(viewsets.ModelViewSet):
queryset = Testresult.objects.all()
serializer_class = UnvalidatedTestresultSerializer
serializers.py:
class UnvalidatedVerdictSerializer(serializers.ModelSerializer):
class Meta:
model = Verdict
fields = ('value',)
extra_kwargs =
'value': 'validators': []
def create(self, validated_data):
try:
return Verdict.objects.get(**validated_data)
except ObjectDoesNotExist:
raise serializers.ValidationError(detail="Unknown result value", code=status.HTTP_400_BAD_REQUEST)
class UnvalidatedTestcaseSerializer(serializers.ModelSerializer):
class Meta:
model = Testcase
fields = ('uid', 'name')
extra_kwargs =
'uid': 'validators': []
def create(self, validated_data):
uid = validated_data.get('uid')
name = validated_data.get('name')
try:
testcase = Testcase.objects.get(uid=uid)
if testcase.name != name:
testcase.name = name
testcase.save()
except ObjectDoesNotExist:
testcase = Testcase.objects.create(**validated_data)
return testcase
class UnvalidatedTestresultSerializer(serializers.ModelSerializer):
testcase = UnvalidatedTestcaseSerializer()
verdict = UnvalidatedVerdictSerializer()
class Meta:
model = Testresult
fields = ('testcase', 'verdict')
def create(self, validated_data):
verdict_data = validated_data.pop('verdict')
serialized_verdict = UnvalidatedVerdictSerializer(data=verdict_data)
serialized_verdict.is_valid(raise_exception=True)
verdict_instance = serialized_verdict.save()
testcase_data = validated_data.pop('testcase')
serialized_testcase = UnvalidatedTestcaseSerializer(data=testcase_data)
serialized_testcase.is_valid(raise_exception=True)
testcase_instance = serialized_testcase.save()
return Testresult.objects.create(testcase=testcase_instance, verdict=verdict_instance)
What I like
Validation mechanisms of ModelSerializer can be utilized.
What I don't like
create methods don't actually create Model instances but merely retrieve and udpate them if necessary.
GET requests will also be using the "Unvalidated" serializers, which I don't see as a problem right now, but maybe that is also not a good practice?
Take Three: combining both approaches and select the serializer class to use depending on the request method
In this approach the same serializer classes as shown in Take One and Take Two will be used, so I'm only giving the code for the View class.
views.py:
class DynamicTestresultViewSet(viewsets.ModelViewSet):
queryset = Testresult.objects.all()
def get_serializer_class(self):
if self.request.method == "POST":
return UnvalidatedTestresultSerializer
else:
return StandardTestresultSerializer
What I like
Serializers are used to parse and validate the input data of the request. GET requests will be served using the "normal" serializer.
What I don't like
Requires six serializer classes instead of three.
What approach would you suggest, or would you do it entirely different?
python django
python django
edited 23 hours ago
dudenr33
asked 23 hours ago
dudenr33dudenr33
2065
2065
add a comment |
add a comment |
0
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f217214%2fdjango-restframework-writable-nested-serializer-with-custom-behavior%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f217214%2fdjango-restframework-writable-nested-serializer-with-custom-behavior%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown