我尝试创建或更新嵌套对象(如果该对象存在),我尝试使用create_or_update方法,现在创建工作正常,但是更新失败,并说pk已经存在。
我的模特:
class ContentHotel(models.Model):
hotel_id = models.IntegerField(unique=True, blank=True, primary_key=True)
star = models.FloatField(blank=True, null=True)
class Meta:
managed = False
db_table = 'content_hotels'
ordering = ('hotel_id',)
def __str__(self):
return str(self.hotel_id)
class RateHotel(models.Model):
rate_hotel_id = models.IntegerField(blank=True, unique=True, primary_key=True)
content_hotel = models.ForeignKey(ContentHotel, on_delete=models.CASCADE, related_name='rate_hotels')
source_code = models.CharField(max_length=20, blank=True, null=True)
class Meta:
managed = False
db_table = 'rate_hotels'
ordering = ('rate_hotel_id',)
def __str__(self):
return str(self.rate_hotel_id)
我的序列化器:
# To handle RateHotel object operations
class RateHotelSerializer(serializers.ModelSerializer):
class Meta:
model = RateHotel
fields = __all__
# To handle nested object operations
class RateHotelSerializerTwo(serializers.ModelSerializer):
class Meta:
model = RateHotel
fields = __all__
read_only_fields = ('content_hotel',)
class ContentHotelSerializer(serializers.ModelSerializer):
rate_hotels = RateHotelSerializerTwo(many=True)
class Meta:
model = ContentHotel
fields = __all__
def create(self, validated_data):
rate_hotels_data = validated_data.pop('rate_hotels')
hotel_id = validated_data.pop('hotel_id')
content_hotel, created = ContentHotel.objects.update_or_create(hotel_id=hotel_id, defaults={**validated_data})
for rate_hotel_data in rate_hotels_data:
rate_hotel_id = rate_hotel_data.pop('rate_hotel_id')
RateHotel.objects.update_or_create(rate_hotel_id=rate_hotel_id, content_hotel=content_hotel,
defaults=rate_hotel_data)
return content_hotel
def update(self, instance, validated_data):
rate_hotels_data = validated_data.pop('rate_hotels')
rate_hotels = list(instance.rate_hotels.all())
for key in validated_data:
instance.key = validated_data.get(key, instance.key)
instance.save()
for rate_hotel_data in rate_hotels_data:
rate_hotel = rate_hotels.pop(0)
for key in rate_hotel_data:
rate_hotel.key = rate_hotel_data.get(key, rate_hotel.key)
rate_hotel.save()
return instance
JSON:
# Post Request - Create:
{
"hotel_id": -1,
"star": null,
"rate_hotels": [{"rate_hotel_id": -1}]
}
# Post Response:
{
"hotel_id": -1,
"star": null,
"rate_hotels": [
{
"content_hotel": -1,
"rate_hotel_id": -1,
"source_code": null,
}
]
}
# Post Request - Update:
{
"hotel_id": -1,
"star": 2,
"rate_hotels": [{"rate_hotel_id": -1, "source_code": "-1"}]
}
{
"hotel_id": [
"content hotel with this hotel id already exists."
],
"rate_hotels": [
{
"rate_hotel_id": [
"rate hotel with this rate hotel id already exists."
]
}
],
"status_code": 400
}
如果我发送放置请求以更新嵌套对象,则内容酒店部分可以正常工作,但是价格酒店部分仍说'rate_hotel_id'已经存在。
有人可以帮助我解决此问题吗?我无法在线找到任何相关来源,在此先感谢!
答案 0 :(得分:0)
嗯,这看起来像您正在寻找“如果存在则另外创建的更新”用例,并且Django有一个get_or_create()
方法来执行您想要的操作,请参见此处的文档以供参考:{{3 }}
但是,对于您来说,它可能看起来像这样:
id = 'some identifier that is unique to the model you wish to lookup'
content_hotel, created = RateHotel.objects.get_or_create(
rate_hotel_id=rate_hotel_id,
content_hotel=content_hotel,
defaults=rate_hotel_data
)
if created:
# means you have created a new content hotel, redirect a success here or whatever you want to do on creation!
else:
# content hotel just refers to the existing one, this is where you can update your Model
我还建议删除hotel_id
和hotel_rate_id
作为主键:
class ContentHotel(models.Model):
hotel_id = models.IntegerField(unique=True, blank=True,)
star = models.FloatField(blank=True, null=True)
class Meta:
managed = False
db_table = 'content_hotels'
ordering = ('hotel_id',)
def __str__(self):
return str(self.hotel_id)
class RateHotel(models.Model):
rate_hotel_id = models.IntegerField(blank=True, unique=True,)
content_hotel = models.ForeignKey(ContentHotel, on_delete=models.CASCADE, related_name='rate_hotels')
source_code = models.CharField(max_length=20, blank=True, null=True)
class Meta:
managed = False
db_table = 'rate_hotels'
ordering = ('rate_hotel_id',)
def __str__(self):
return str(self.rate_hotel_id)
update_or_create()
方法不接受主键,但可以接受其他唯一的数据库字段来执行更新:
因此,update_or_create()
看起来像这样:
obj, created = Person.objects.update_or_create(
first_name='John', last_name='Lennon',
defaults={'first_name': 'Bob'},
)
答案 1 :(得分:0)
您的create
代码看起来不错,但是您的update
代码存在一些更大的问题,在我们知道真正的问题所在之前,您需要解决这些问题。
for key in validated_data:
instance.key = validated_data.get(key, instance.key)
instance.save()
在这里,您试图遍历验证数据中的值,并将该键的值分配给模型上的该属性。您实际上正在做的是将模型上的属性key
设置为validated_data
中每个键的值。为了更好地说明问题,我将举一个纠正问题的示例:
for key in validated_data:
setattr(instance, key, validated_data.get(key, getattr(instance, key))
instance.save()
这里,我们没有尝试设置instance.key
,而是使用python内置方法setattr
来设置名称与变量key
的值匹配的属性,我们从validated_data
撤出。
我不确定是否可以解决此问题。我将确保您将请求正确发送到具有正确标头值的正确url,以获取更新方法。 Django Rest Framework可能阻止了幕后的创建,因为它认为您正在将请求作为创建操作发送,并且已经找到了主键。