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;








2












$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?










share|improve this question











$endgroup$


















    2












    $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?










    share|improve this question











    $endgroup$














      2












      2








      2





      $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?










      share|improve this question











      $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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 23 hours ago







      dudenr33

















      asked 23 hours ago









      dudenr33dudenr33

      2065




      2065




















          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
          );



          );













          draft saved

          draft discarded


















          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















          draft saved

          draft discarded
















































          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.




          draft saved


          draft discarded














          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





















































          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







          Popular posts from this blog

          名間水力發電廠 目录 沿革 設施 鄰近設施 註釋 外部連結 导航菜单23°50′10″N 120°42′41″E / 23.83611°N 120.71139°E / 23.83611; 120.7113923°50′10″N 120°42′41″E / 23.83611°N 120.71139°E / 23.83611; 120.71139計畫概要原始内容臺灣第一座BOT 模式開發的水力發電廠-名間水力電廠名間水力發電廠 水利署首件BOT案原始内容《小檔案》名間電廠 首座BOT水力發電廠原始内容名間電廠BOT - 經濟部水利署中區水資源局

          格濟夫卡 參考資料 导航菜单51°3′40″N 34°2′21″E / 51.06111°N 34.03917°E / 51.06111; 34.03917ГезівкаПогода в селі 编辑或修订

          聖斯德望教堂 (塞克什白堡) 參考資料 导航菜单