django + tastypie:我如何 POST 在不获取 "duplicate key value violates unique constraint" 的情况下替换数据
django + tastypie: How do I POST to replace data without getting "duplicate key value violates unique constraint"
我正在尝试使用 tastypie 为我的 Django 项目编写 REST API。我可以用它来 POST 一些数据:
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
那么,我哪里错了?
谢谢
好的,所以我最后包括了一个名称检查,然后是 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}
我对其他解决方案持开放态度,但如果其他人有任何更好的想法...
您必须针对单个资源,例如:
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 > 然后它应该工作。
您可以将环境名称公开为端点,这样您的资源 url 将变为 http://localhost:8000/api/v1/report_status/[environment_name]
,然后使用 PUT
调用,这将更新或创建新资源取决于给定环境名称的资源是否存在。
我正在尝试使用 tastypie 为我的 Django 项目编写 REST API。我可以用它来 POST 一些数据:
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
那么,我哪里错了?
谢谢
好的,所以我最后包括了一个名称检查,然后是 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}
我对其他解决方案持开放态度,但如果其他人有任何更好的想法...
您必须针对单个资源,例如:
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 > 然后它应该工作。
您可以将环境名称公开为端点,这样您的资源 url 将变为 http://localhost:8000/api/v1/report_status/[environment_name]
,然后使用 PUT
调用,这将更新或创建新资源取决于给定环境名称的资源是否存在。