I am trying to write a REST API for my Django project using tastypie. I can use it to POST some data:
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/
which works as it gets put into the database, but when I come to send a second set of data with the enviroment name (i.e. resend the same request), with the intention of replace the first set of data sent, I get the following error (abbreviated):
{"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.
I understand that I have set the environment name to be unique, but I'm trying to replace the data, not upload another environment with the same name. The problem seems to be that the id is auto-incrementing. I do not wish to have to provide an id every time - I want the end user to simply supply an environment name and have it replace the data if it is already in the database. Can anyone tell me how this is usually done?
Below is the relevant parts of the code. I have a foreign key which I'm not sure whether or not complicates things, or if this is another matter entirely.
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
So, where am I going wrong?
Thanks
You have to target the single resource e.g:
And I think that you need a "PUT" request instead of "POST"
So after you create the environment, you get it's id, you send a "PUT" request to "http://localhost:8000/api/v1/report_status/< ID > and then it should work.