Django Rest Framework - Create and Update Parent-Child relation
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
add a comment |
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
add a comment |
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
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
python django django-rest-framework django-serializer
asked Jan 3 at 10:14
Wouter GoossensWouter Goossens
626
626
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
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
add a comment |
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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
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%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
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
add a comment |
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
add a comment |
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
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
answered Jan 3 at 11:14
Ken4scholarsKen4scholars
1,5682917
1,5682917
add a comment |
add a comment |
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
add a comment |
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
add a comment |
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
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
answered Jan 3 at 11:57
qinglin xiaoqinglin xiao
11
11
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
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%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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
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');
});
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');
});
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