POST to create a record with a foreign key to an other model

5,146 views
Skip to first unread message

Paul Amalou

unread,
Jul 31, 2011, 11:39:39 PM7/31/11
to Tastypie
I figured out how to write client api for the GET POST PUT and DELETE
verbs for simple models with no relationship to an other model. I am
struggling to do figure out a POST for a resource with a foreign key.

Models (abbridged)

class Contact(models.Model):
....

class Test(models.Model):
name = models.CharField(max_length=30)
engineer = models.ForeignKey(Contact)

class TestResult(models.Model):
test = models.ForeignKey(Test)
comment = models.TextField()
duration = models.IntegerField()
runat = models.DateTimeField()
result = models.CharField(max_length=7, choices=result_choices)

Resources

class TestResource(ModelResource):
enginneer = fields.ForeignKey(ContactResource, 'enginneer')
class Meta:
queryset = Test.objects.all()
resource_name = 'test'
authorization= Authorization()
allowed_methods = ['get']

class TestResultResource(ModelResource):
test = fields.ForeignKey(TestResource, 'test')
class Meta:
queryset = TestResult.objects.all()
resource_name = 'testresult'
authorization= Authorization()
allowed_methods = ['get', 'post']

urls:

contact_resource = ContactResource()
test_resource = TestResource()
testresult_resource = TestResultResource()

urlpatterns = patterns(''
url(r'^api/', include(test_resource.urls)),
url(r'^api/', include(testresult_resource.urls)),
)

class TestResultClient:
def __init__(self, server, port):
self.address = '{0}:{1}'.format(server, port)
self.http = httplib2.Http()

def get(self, testresult_id):
url = 'http://{0}/api/testresult/{1}/'.format(self.address,
testresult_id)
return self.http.request(url)

def create(self, data):
url = 'http://{0}/api/testresult/'.format(self.address)
headers = {'Content-type': 'application/json'}
data = json.dumps(data)
return self.http.request(url, 'POST', headers=headers,
body=data)


def create(client):
now = datetime.datetime.now()
runat = datetime.datetime(now.year, now.month, now.day, now.hour,
now.minute, now.second)

testresult = {
"test.id": "2",
"result": "Fatal",
"comment": "Http Server error",
"duration": 600,
"runat": str(runat),
}

status, content = client.create(testresult)
print('{0}: status {1}'.format('POST', status['status']))
print content


All my attempts at creating a TestResult fail. I do not know how the
foreign key is specified.
Is it
1) "testresult.test_id": "2"?
2) "testresult.test.id": "2"?
3) "test.id": "2"?
4) "test": "/api/test/2/"?

None work. Any help is appreciated :)
Thanks



Daniel Lindsley

unread,
Aug 15, 2011, 1:46:54 AM8/15/11
to django-...@googlegroups.com
Paul,


You want to be sending either a URI or a dictionary. I'd guess that
you'd need to use either ``"test": "/api/v1/test/2/",`` or ``"test":
{"pk": 2},`` ought to work. For an example, do a GET on the resource
you're trying to post to. Whatever format you get is what you should
be able to send back.


Daniel

ycseattle

unread,
Oct 20, 2011, 1:18:48 PM10/20/11
to django-...@googlegroups.com
Hello,

I am running into the same problem with almost the same data model (mine is a foreignkey relationship between a calendar item and the Django User). I tried the solution above but still doesn't work.

class UserResource(ModelResource):
    class Meta:
        queryset = User.objects.all()
        resource_name = 'user'
        excludes = ['email', 'password', 'is_superuser']
        
        authorization = Authorization()   

class EventResource(ModelResource):
    account_id = fields.ForeignKey(UserResource, 'user')
    
    class Meta:
        queryset = Event.objects.all()
        resource_name = 'event'        
        allowed_methods = ['get', 'post'] 


This is my post command, note that for the foreignkey field, I used "account_id":"/api/ttt/user/2/"

curl --dump-header - -H "Content-Type: application/json" -X POST --data '{"dtstart":"2011-10-20 02:45:25", "dtend":"2012-10-10 11:11:11", "account_id":"/api/ttt/user/2/", "summary":"summary", "detail":"some detail info"}' http://127.0.0.1:8000/api/ttt/event/

Help please!


Greg McGuire

unread,
Oct 20, 2011, 1:31:04 PM10/20/11
to django-...@googlegroups.com
Can you also post (haha) your error message and the api registration code you're using?

Greg

ycseattle

unread,
Oct 20, 2011, 5:45:09 PM10/20/11
to django-...@googlegroups.com
Hi Greg,

This is the api registration code in urls.py:
ttt_api = Api(api_name = 'ttt')
ttt_api.register(UserResource())
ttt_api.register(EventResource())


This is the Django model definitions for Event, which has a foreign key to the User table. The column name for the foreignkey column is "account_id", that's why I defined "account_id" as foreignkey in tastypie EventResource. 

from django.contrib.auth.models import User
class Event(models.Model):
    account     = models.ForeignKey(User)
    date        = models.DateField('date')
    summary     = models.CharField('summary', max_length=250,null=False)
    detail      = models.CharField('detail', max_length=2000, null=False, default='')
        
    class Meta:
        db_table = 'ttt_event'


The api path is correct as I can see the correct value by going to http://127.0.0.1:8000/api/ttt/user/2/?format=json
If I specify the POST data for "account_id":{"id":2}, or "account_id":"/api/ttt/user/2/", I got this IntegrityError:
line 36, in defaulterrorhandler\n raise errorclass, errorvalue\n\nIntegrityError: (1048, \"Column 'account_id' cannot be null\")\n"}

Been stuck for a while, please help!

Greg McGuire

unread,
Oct 21, 2011, 9:48:27 AM10/21/11
to django-...@googlegroups.com
Hello -

In your Django model of Event your foreign key is the "account" field, so in tastypie it should be the same by default (when using the Model-mirroring ModelResource.)  Further, Django uses the [attribute_name]_id attribute internally, and I think this might be causing interference with tastypie when trying to hydrate a new Event.

Try renaming account_id to just account on your EventResource.

Best,
Greg

ycseattle

unread,
Oct 22, 2011, 7:04:23 PM10/22/11
to django-...@googlegroups.com
Thanks for the answer. Problem solved!
Reply all
Reply to author
Forward
0 new messages