django + sweetpie:我如何发布以替换数据而不得到“重复键值违反唯一约束”
Posted
技术标签:
【中文标题】django + sweetpie:我如何发布以替换数据而不得到“重复键值违反唯一约束”【英文标题】:django + tastypie: How do I POST to replace data without getting "duplicate key value violates unique constraint" 【发布时间】:2015-09-11 23:22:27 【问题描述】:我正在尝试使用tastepie 为我的Django 项目编写一个REST API。我可以用它来发布一些数据:
curl --dump-header - -H "Content-Type: application/json" -X POST --data '"name": "environment1", "last_active": "2015-06-18T15:56:37"' http://localhost:8000/api/v1/report_status/
它在放入数据库时起作用,但是当我来发送具有环境名称的第二组数据(即重新发送相同的请求)时,为了替换发送的第一组数据,我得到以下错误(略):
"error_message": "duplicate key value violates unique constraint \"<project_name>_environment_name_key\"\nDETAIL: Key (name)=(production) already exists.\n", "traceback": "Traceback ... django.db.utils.IntegrityError: duplicate key value violates unique constraint \"oilserver_environment_name_key\"\nDETAIL: Key (name)=(production) already exists.
我了解我已将环境名称设置为唯一,但我正在尝试替换数据,而不是上传另一个具有相同名称的环境。问题似乎是 id 是自动递增的。我不希望每次都提供一个 id - 我希望最终用户简单地提供一个环境名称并让它替换已经在数据库中的数据。谁能告诉我这通常是怎么做的?
下面是代码的相关部分。我有一个外键,我不确定它是否会使事情复杂化,或者这是否完全是另一回事。
models.py:
from django.db import models
class EnvironmentState(models.Model):
name = models.CharField(
max_length=255,
default="Unknown",
help_text="Current state of the environment.")
description = models.TextField(
default=None,
blank=True,
null=True,
help_text="Optional description for state.")
def __str__(self):
return self.name
class Environment(models.Model):
name = models.CharField(
max_length=255,
unique=True,
help_text="Name of environment")
last_active = models.DateTimeField(
default=None,
blank=True,
null=True,
help_text="DateTime when environment message was last received.")
current_situation = models.TextField(
help_text="Statement(s) giving background to the current env status.")
status = models.ForeignKey(EnvironmentState)
def __str__(self):
return self.name
resources.py:
from tastypie import fields
from tastypie.resources import ModelResource
from tastypie.authorization import Authorization
from oilserver.models import Environment, EnvironmentState
from oilserver.status_checker import StatusChecker
class EnvironmentStateResource(ModelResource):
class Meta:
queryset = EnvironmentState.objects.all()
resource_name = 'environment_state'
authorization = Authorization()
class ReportStatusResource(ModelResource):
status = fields.ForeignKey(EnvironmentStateResource, 'status',
null=True, full=True)
class Meta:
queryset = Environment.objects.all()
resource_name = 'report_status'
authorization = Authorization()
def hydrate(self, bundle):
name = bundle.data.get('name')
last_active = bundle.data.get('last_active')
status_checker = StatusChecker(last_active)
# StatusChecker is just a class that takes in some data and
# generates a 'state' (up, down) and a 'situation' string explaining
# to the user what is going on.
bundle.data['current_situation'] = status_checker.situation
env_state = EnvironmentState.objects.get(name=status_checker.state)
bundle.data['status'] = "pk": env_state.pk
return bundle
那么,我哪里错了?
谢谢
【问题讨论】:
【参考方案1】:好的,所以我最终包含了一个名称检查,然后是 bundle.data['id'] = env.id 如果它已经存在:
class ReportStatusResource(ModelResource):
status = fields.ForeignKey(EnvironmentStateResource, 'status',
null=True, full=True)
class Meta:
queryset = Environment.objects.all()
resource_name = 'report_status'
authorization = Authorization()
def hydrate(self, bundle):
name = bundle.data.get('name')
for env in Environment.objects.all():
if env.name == name:
bundle.data['id'] = env.id
last_active = bundle.data.get('last_active')
status_checker = StatusChecker(last_active)
# StatusChecker is just a class that takes in some data and
# generates a 'state' (up, down) and a 'situation' string explaining
# to the user what is going on.
bundle.data['current_situation'] = status_checker.situation
env_state = EnvironmentState.objects.get(name=status_checker.state)
bundle.data['status'] = "pk": env_state.pk
我对其他解决方案持开放态度,但如果其他人有更好的想法......
【讨论】:
【参考方案2】:您必须针对单个资源,例如:
http://localhost:8000/api/v1/report_status/<IDENTIFIER OF THE RESOURCE YOU WANT TO UPDATE>
我认为你需要一个“PUT”请求而不是“POST”
所以在你创建环境之后,你得到它的 id,你发送一个“PUT”请求到“http://localhost:8000/api/v1/report_status/
【讨论】:
是的,你是对的。我需要把 id 放在最后。谢谢【参考方案3】:您可以将环境名称公开为端点,这样您的资源 url 将变为 http://localhost:8000/api/v1/report_status/[environment_name]
,然后使用 PUT
调用,这将根据给定环境名称的资源是否更新或创建新资源存在与否。
Tastypie docs about exposing non PK endpoints
Tastypie source code for put_details
【讨论】:
以上是关于django + sweetpie:我如何发布以替换数据而不得到“重复键值违反唯一约束”的主要内容,如果未能解决你的问题,请参考以下文章