Django Rest Framework - Create and Update Parent-Child relation












1















I'm trying to use the Django Rest Framework serializers to make API's for my front-end to create / update and delete articles in a store. These articles can have multiple prices (depending on time). So there is a one-to-many relation from article (one) to price (many). I have defined this in models.py:



class Article(models.Model):
article_id = models.AutoField(primary_key=True, verbose_name="Article ID")
article_name = models.CharField(max_length=250, verbose_name="Name")
article_order = models.IntegerField(verbose_name="Order")

class Price(models.Model):
price_id = models.AutoField(primary_key=True, verbose_name="Price ID")
article_id = models.ForeignKey(Article, on_delete=models.CASCADE, verbose_name="Article ID", related_name="Prices")
price_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="Price")


My serializers.py file looks like this:



from rest_framework import serializers
from .models import *

class PriceSerializer(serializers.ModelSerializer):

class Meta:
model = Price
fields = ('price_price',)

class ArticleSerializer(serializers.ModelSerializer):

Prices = PriceSerializer(many=True)

def create(self, validated_data):
prices_data = validated_data.pop('Prices')
article = Article.objects.create(**validated_data)
for price_data in prices_data:
Price.objects.create(article_id=article, **price_data)
return article

def update(self, instance, validated_data):
prices_data = validated_data.pop('Prices')
Article.objects.filter(article_id=instance.article_id).update(**validated_data)
for price_data in prices_data:
Price.objects.get_or_create(article_id=instance, **price_data)
return instance

class Meta:
model = Article
fields = '__all__'


This works perfectly and I can create a new article-price measurement with this data: (article_order will be used later for ordering the list)



{"Prices":[{"price_price":"1"}],"article_name":"Article A","article_order":"1"}


Until this point, everything is working as expected. But when I try to update the prices, the Price.objects.get_or_create() statement does not recognize existing prices. For example: when the first price is €10, then €20 and then again €10, the last price will not be inserted because the get_or_create statement does not recognize this as a new instance of the price model. That probably makes sense, because the PriceSerializer does not serialize the price_id. But when I change the serializer to:



class PriceSerializer(serializers.ModelSerializer):

class Meta:
model = Price
fields = '__all__


, I can no longer create instances because the ArticleSerializer is requiring an article_id for the Price instance (but that doesn't exist yet).



Does anyone have an idea how to solve this problem? The DRF documentation only includes the create statement for this type of nested serializer.



https://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers










share|improve this question



























    1















    I'm trying to use the Django Rest Framework serializers to make API's for my front-end to create / update and delete articles in a store. These articles can have multiple prices (depending on time). So there is a one-to-many relation from article (one) to price (many). I have defined this in models.py:



    class Article(models.Model):
    article_id = models.AutoField(primary_key=True, verbose_name="Article ID")
    article_name = models.CharField(max_length=250, verbose_name="Name")
    article_order = models.IntegerField(verbose_name="Order")

    class Price(models.Model):
    price_id = models.AutoField(primary_key=True, verbose_name="Price ID")
    article_id = models.ForeignKey(Article, on_delete=models.CASCADE, verbose_name="Article ID", related_name="Prices")
    price_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="Price")


    My serializers.py file looks like this:



    from rest_framework import serializers
    from .models import *

    class PriceSerializer(serializers.ModelSerializer):

    class Meta:
    model = Price
    fields = ('price_price',)

    class ArticleSerializer(serializers.ModelSerializer):

    Prices = PriceSerializer(many=True)

    def create(self, validated_data):
    prices_data = validated_data.pop('Prices')
    article = Article.objects.create(**validated_data)
    for price_data in prices_data:
    Price.objects.create(article_id=article, **price_data)
    return article

    def update(self, instance, validated_data):
    prices_data = validated_data.pop('Prices')
    Article.objects.filter(article_id=instance.article_id).update(**validated_data)
    for price_data in prices_data:
    Price.objects.get_or_create(article_id=instance, **price_data)
    return instance

    class Meta:
    model = Article
    fields = '__all__'


    This works perfectly and I can create a new article-price measurement with this data: (article_order will be used later for ordering the list)



    {"Prices":[{"price_price":"1"}],"article_name":"Article A","article_order":"1"}


    Until this point, everything is working as expected. But when I try to update the prices, the Price.objects.get_or_create() statement does not recognize existing prices. For example: when the first price is €10, then €20 and then again €10, the last price will not be inserted because the get_or_create statement does not recognize this as a new instance of the price model. That probably makes sense, because the PriceSerializer does not serialize the price_id. But when I change the serializer to:



    class PriceSerializer(serializers.ModelSerializer):

    class Meta:
    model = Price
    fields = '__all__


    , I can no longer create instances because the ArticleSerializer is requiring an article_id for the Price instance (but that doesn't exist yet).



    Does anyone have an idea how to solve this problem? The DRF documentation only includes the create statement for this type of nested serializer.



    https://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers










    share|improve this question

























      1












      1








      1








      I'm trying to use the Django Rest Framework serializers to make API's for my front-end to create / update and delete articles in a store. These articles can have multiple prices (depending on time). So there is a one-to-many relation from article (one) to price (many). I have defined this in models.py:



      class Article(models.Model):
      article_id = models.AutoField(primary_key=True, verbose_name="Article ID")
      article_name = models.CharField(max_length=250, verbose_name="Name")
      article_order = models.IntegerField(verbose_name="Order")

      class Price(models.Model):
      price_id = models.AutoField(primary_key=True, verbose_name="Price ID")
      article_id = models.ForeignKey(Article, on_delete=models.CASCADE, verbose_name="Article ID", related_name="Prices")
      price_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="Price")


      My serializers.py file looks like this:



      from rest_framework import serializers
      from .models import *

      class PriceSerializer(serializers.ModelSerializer):

      class Meta:
      model = Price
      fields = ('price_price',)

      class ArticleSerializer(serializers.ModelSerializer):

      Prices = PriceSerializer(many=True)

      def create(self, validated_data):
      prices_data = validated_data.pop('Prices')
      article = Article.objects.create(**validated_data)
      for price_data in prices_data:
      Price.objects.create(article_id=article, **price_data)
      return article

      def update(self, instance, validated_data):
      prices_data = validated_data.pop('Prices')
      Article.objects.filter(article_id=instance.article_id).update(**validated_data)
      for price_data in prices_data:
      Price.objects.get_or_create(article_id=instance, **price_data)
      return instance

      class Meta:
      model = Article
      fields = '__all__'


      This works perfectly and I can create a new article-price measurement with this data: (article_order will be used later for ordering the list)



      {"Prices":[{"price_price":"1"}],"article_name":"Article A","article_order":"1"}


      Until this point, everything is working as expected. But when I try to update the prices, the Price.objects.get_or_create() statement does not recognize existing prices. For example: when the first price is €10, then €20 and then again €10, the last price will not be inserted because the get_or_create statement does not recognize this as a new instance of the price model. That probably makes sense, because the PriceSerializer does not serialize the price_id. But when I change the serializer to:



      class PriceSerializer(serializers.ModelSerializer):

      class Meta:
      model = Price
      fields = '__all__


      , I can no longer create instances because the ArticleSerializer is requiring an article_id for the Price instance (but that doesn't exist yet).



      Does anyone have an idea how to solve this problem? The DRF documentation only includes the create statement for this type of nested serializer.



      https://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers










      share|improve this question














      I'm trying to use the Django Rest Framework serializers to make API's for my front-end to create / update and delete articles in a store. These articles can have multiple prices (depending on time). So there is a one-to-many relation from article (one) to price (many). I have defined this in models.py:



      class Article(models.Model):
      article_id = models.AutoField(primary_key=True, verbose_name="Article ID")
      article_name = models.CharField(max_length=250, verbose_name="Name")
      article_order = models.IntegerField(verbose_name="Order")

      class Price(models.Model):
      price_id = models.AutoField(primary_key=True, verbose_name="Price ID")
      article_id = models.ForeignKey(Article, on_delete=models.CASCADE, verbose_name="Article ID", related_name="Prices")
      price_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="Price")


      My serializers.py file looks like this:



      from rest_framework import serializers
      from .models import *

      class PriceSerializer(serializers.ModelSerializer):

      class Meta:
      model = Price
      fields = ('price_price',)

      class ArticleSerializer(serializers.ModelSerializer):

      Prices = PriceSerializer(many=True)

      def create(self, validated_data):
      prices_data = validated_data.pop('Prices')
      article = Article.objects.create(**validated_data)
      for price_data in prices_data:
      Price.objects.create(article_id=article, **price_data)
      return article

      def update(self, instance, validated_data):
      prices_data = validated_data.pop('Prices')
      Article.objects.filter(article_id=instance.article_id).update(**validated_data)
      for price_data in prices_data:
      Price.objects.get_or_create(article_id=instance, **price_data)
      return instance

      class Meta:
      model = Article
      fields = '__all__'


      This works perfectly and I can create a new article-price measurement with this data: (article_order will be used later for ordering the list)



      {"Prices":[{"price_price":"1"}],"article_name":"Article A","article_order":"1"}


      Until this point, everything is working as expected. But when I try to update the prices, the Price.objects.get_or_create() statement does not recognize existing prices. For example: when the first price is €10, then €20 and then again €10, the last price will not be inserted because the get_or_create statement does not recognize this as a new instance of the price model. That probably makes sense, because the PriceSerializer does not serialize the price_id. But when I change the serializer to:



      class PriceSerializer(serializers.ModelSerializer):

      class Meta:
      model = Price
      fields = '__all__


      , I can no longer create instances because the ArticleSerializer is requiring an article_id for the Price instance (but that doesn't exist yet).



      Does anyone have an idea how to solve this problem? The DRF documentation only includes the create statement for this type of nested serializer.



      https://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers







      python django django-rest-framework django-serializer






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Jan 3 at 10:14









      Wouter GoossensWouter Goossens

      626




      626
























          2 Answers
          2






          active

          oldest

          votes


















          1














          You're not using the get_or_create method correctly.
          It accepts kwargs which are used to lookup the object and if it does not exist, creates them, adding other field values from defaults.



          This means that if you pass the new values to update as kwargs, it won't find such object because it currently has different values for those fields.
          Instead, you should pass in fields that do not change in kwargs and the rest in defaults.



          This is what you should do



          def update(self, instance, validated_data):
          prices_data = validated_data.pop('Prices')
          Article.objects.filter(article_id=instance.article_id).update(**validated_data)
          for price_data in prices_data:
          Price.objects.get_or_create(article_id=instance, defaults=price_data)
          return instance


          Note however, that you may get a MutipleObjectsReturned error since an article can have several prices based on your DB architecture. You may want to change the article_id to a OneToOneField to prevent this if mutiple prices per Article was not intended.



          You can read more about get_or_create in the Django docs



          As a side note, in OOP architecture id in Article already implies article id, so prepending article to your field names is a really redundant and bad practice. And again, you may want to use the Django autogenerated id field instead of redefining it, unless you want to customize it in some way, which is currently not obvious in your code






          share|improve this answer































            0
















            1. you can see django rest framework document:https://www.django-rest-framework.org/api-guide/serializers/



              def update(self, instance, validated_data):
              instance.email = validated_data.get('email', instance.email)
              instance.content = validated_data.get('content', instance.content)
              instance.created = validated_data.get('created', instance.created)
              return instance




            I hope can help you






            share|improve this answer























              Your Answer






              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: "1"
              };
              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: true,
              noModals: true,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: 10,
              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%2fstackoverflow.com%2fquestions%2f54020194%2fdjango-rest-framework-create-and-update-parent-child-relation%23new-answer', 'question_page');
              }
              );

              Post as a guest















              Required, but never shown

























              2 Answers
              2






              active

              oldest

              votes








              2 Answers
              2






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes









              1














              You're not using the get_or_create method correctly.
              It accepts kwargs which are used to lookup the object and if it does not exist, creates them, adding other field values from defaults.



              This means that if you pass the new values to update as kwargs, it won't find such object because it currently has different values for those fields.
              Instead, you should pass in fields that do not change in kwargs and the rest in defaults.



              This is what you should do



              def update(self, instance, validated_data):
              prices_data = validated_data.pop('Prices')
              Article.objects.filter(article_id=instance.article_id).update(**validated_data)
              for price_data in prices_data:
              Price.objects.get_or_create(article_id=instance, defaults=price_data)
              return instance


              Note however, that you may get a MutipleObjectsReturned error since an article can have several prices based on your DB architecture. You may want to change the article_id to a OneToOneField to prevent this if mutiple prices per Article was not intended.



              You can read more about get_or_create in the Django docs



              As a side note, in OOP architecture id in Article already implies article id, so prepending article to your field names is a really redundant and bad practice. And again, you may want to use the Django autogenerated id field instead of redefining it, unless you want to customize it in some way, which is currently not obvious in your code






              share|improve this answer




























                1














                You're not using the get_or_create method correctly.
                It accepts kwargs which are used to lookup the object and if it does not exist, creates them, adding other field values from defaults.



                This means that if you pass the new values to update as kwargs, it won't find such object because it currently has different values for those fields.
                Instead, you should pass in fields that do not change in kwargs and the rest in defaults.



                This is what you should do



                def update(self, instance, validated_data):
                prices_data = validated_data.pop('Prices')
                Article.objects.filter(article_id=instance.article_id).update(**validated_data)
                for price_data in prices_data:
                Price.objects.get_or_create(article_id=instance, defaults=price_data)
                return instance


                Note however, that you may get a MutipleObjectsReturned error since an article can have several prices based on your DB architecture. You may want to change the article_id to a OneToOneField to prevent this if mutiple prices per Article was not intended.



                You can read more about get_or_create in the Django docs



                As a side note, in OOP architecture id in Article already implies article id, so prepending article to your field names is a really redundant and bad practice. And again, you may want to use the Django autogenerated id field instead of redefining it, unless you want to customize it in some way, which is currently not obvious in your code






                share|improve this answer


























                  1












                  1








                  1







                  You're not using the get_or_create method correctly.
                  It accepts kwargs which are used to lookup the object and if it does not exist, creates them, adding other field values from defaults.



                  This means that if you pass the new values to update as kwargs, it won't find such object because it currently has different values for those fields.
                  Instead, you should pass in fields that do not change in kwargs and the rest in defaults.



                  This is what you should do



                  def update(self, instance, validated_data):
                  prices_data = validated_data.pop('Prices')
                  Article.objects.filter(article_id=instance.article_id).update(**validated_data)
                  for price_data in prices_data:
                  Price.objects.get_or_create(article_id=instance, defaults=price_data)
                  return instance


                  Note however, that you may get a MutipleObjectsReturned error since an article can have several prices based on your DB architecture. You may want to change the article_id to a OneToOneField to prevent this if mutiple prices per Article was not intended.



                  You can read more about get_or_create in the Django docs



                  As a side note, in OOP architecture id in Article already implies article id, so prepending article to your field names is a really redundant and bad practice. And again, you may want to use the Django autogenerated id field instead of redefining it, unless you want to customize it in some way, which is currently not obvious in your code






                  share|improve this answer













                  You're not using the get_or_create method correctly.
                  It accepts kwargs which are used to lookup the object and if it does not exist, creates them, adding other field values from defaults.



                  This means that if you pass the new values to update as kwargs, it won't find such object because it currently has different values for those fields.
                  Instead, you should pass in fields that do not change in kwargs and the rest in defaults.



                  This is what you should do



                  def update(self, instance, validated_data):
                  prices_data = validated_data.pop('Prices')
                  Article.objects.filter(article_id=instance.article_id).update(**validated_data)
                  for price_data in prices_data:
                  Price.objects.get_or_create(article_id=instance, defaults=price_data)
                  return instance


                  Note however, that you may get a MutipleObjectsReturned error since an article can have several prices based on your DB architecture. You may want to change the article_id to a OneToOneField to prevent this if mutiple prices per Article was not intended.



                  You can read more about get_or_create in the Django docs



                  As a side note, in OOP architecture id in Article already implies article id, so prepending article to your field names is a really redundant and bad practice. And again, you may want to use the Django autogenerated id field instead of redefining it, unless you want to customize it in some way, which is currently not obvious in your code







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Jan 3 at 11:14









                  Ken4scholarsKen4scholars

                  1,5682917




                  1,5682917

























                      0
















                      1. you can see django rest framework document:https://www.django-rest-framework.org/api-guide/serializers/



                        def update(self, instance, validated_data):
                        instance.email = validated_data.get('email', instance.email)
                        instance.content = validated_data.get('content', instance.content)
                        instance.created = validated_data.get('created', instance.created)
                        return instance




                      I hope can help you






                      share|improve this answer




























                        0
















                        1. you can see django rest framework document:https://www.django-rest-framework.org/api-guide/serializers/



                          def update(self, instance, validated_data):
                          instance.email = validated_data.get('email', instance.email)
                          instance.content = validated_data.get('content', instance.content)
                          instance.created = validated_data.get('created', instance.created)
                          return instance




                        I hope can help you






                        share|improve this answer


























                          0












                          0








                          0









                          1. you can see django rest framework document:https://www.django-rest-framework.org/api-guide/serializers/



                            def update(self, instance, validated_data):
                            instance.email = validated_data.get('email', instance.email)
                            instance.content = validated_data.get('content', instance.content)
                            instance.created = validated_data.get('created', instance.created)
                            return instance




                          I hope can help you






                          share|improve this answer















                          1. you can see django rest framework document:https://www.django-rest-framework.org/api-guide/serializers/



                            def update(self, instance, validated_data):
                            instance.email = validated_data.get('email', instance.email)
                            instance.content = validated_data.get('content', instance.content)
                            instance.created = validated_data.get('created', instance.created)
                            return instance




                          I hope can help you







                          share|improve this answer












                          share|improve this answer



                          share|improve this answer










                          answered Jan 3 at 11:57









                          qinglin xiaoqinglin xiao

                          11




                          11






























                              draft saved

                              draft discarded




















































                              Thanks for contributing an answer to Stack Overflow!


                              • 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.


                              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%2fstackoverflow.com%2fquestions%2f54020194%2fdjango-rest-framework-create-and-update-parent-child-relation%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

                              Mossoró

                              Error while reading .h5 file using the rhdf5 package in R

                              Pushsharp Apns notification error: 'InvalidToken'