Including child resources in a Django Tastypie API
Asked Answered
S

2

8

I'm planning a site with Django and Tastypie for the REST API, and I'm having a tough time figuring out the "right" way to include child resources in a returned resource.

As a sandbox, I made a small app with a Ticket model and a TicketComment model, where comments belong to a ticket. I looked at the Tastypie Cookbook recipe on nested resources (http://django-tastypie.readthedocs.org/en/latest/cookbook.html#nested-resources), but I'm having a hard time understanding why I should do that. The code below uses django.forms.models.model_to_dict() to get the comments into the ticket, but I'm thinking there must be a "gotcha" here somewhere.

Is there a reason I shouldn't do what I'm doing now? Also, is there a cleaner-feeling pattern for this than what's listed in the cookbook?

Models are as follows:

# tickets/models.py

from django.db import models

class Ticket(models.Model):
    title = models.CharField(max_length=200)
    create_ts = models.DateTimeField(auto_now_add=True)
    submitter_email = models.EmailField()
    PRIORITY_CHOICES = (
        ('H', 'High'),
        ('M', 'Medium'),
        ('L', 'Low'),)
    priority = models.CharField(max_length=1, choices=PRIORITY_CHOICES)
    description = models.TextField()
    STATUS_CHOICES = (
        ('NEW', 'New & Unclaimed'),
        ('WIP', 'Work In Progress'),
        ('RES', 'Resolved'),
        ('CLS', 'Closed'),)
    status = models.CharField(max_length=3, choices=STATUS_CHOICES)

    def __unicode__(self):
        return "<Ticket:%d:%s>" % (self.id, self.title,)

class TicketComment(models.Model):
    ticket = models.ForeignKey(Ticket)
    comment_ts = models.DateTimeField(auto_now_add=True)
    commenter_email = models.EmailField()
    comment = models.TextField()

    def __unicode__(self):
        return "<TicketComment:%d:%d>" % (self.ticket.id, self.id,)

Resources are as follows:

# tickets/api.py

from tastypie import fields
from tastypie.resources import ModelResource
from tickets.models import Ticket, TicketComment
from django.forms.models import model_to_dict

class TicketResource(ModelResource):

    class Meta:
        queryset = Ticket.objects.all()
        resource_name = 'ticket'

    def dehydrate(self, bundle):
        comments = TicketComment.objects.filter(ticket=bundle.data['id'])
        bundle.data['comments'] = [model_to_dict(c) for c in comments]
        return bundle

class TicketCommentResource(ModelResource):
    ticket = fields.ForeignKey(TicketResource, 'ticket')

    class Meta:
        queryset = TicketComment.objects.all()
        resource_name = 'comment'

Output is as follows:

{
   comments: [
        {
            comment: "This is the first comment.",
            commenter_email: "[email protected]",
            id: 1,
            ticket: 1
        },
        {
            comment: "This is the second comment.",
            commenter_email: "[email protected]",
            id: 2,
            ticket: 1
        }
    ],
    create_ts: "2011-10-17T15:55:11.372000",
    description: "This is the first ticket.",
    id: "1",
    priority: "M",
    resource_uri: "/api/v1/ticket/1/",
    status: "NEW",
    submitter_email: "[email protected]",
    title: "First Ticket"
}
Sultanate answered 19/10, 2011 at 2:0 Comment(0)
E
14

You're looking for related fields: http://django-tastypie.readthedocs.org/en/latest/fields.html#relationship-fields

Evaporation answered 19/10, 2011 at 2:19 Comment(3)
Thanks, this makes a lot more sense, however all I'm getting is the URL to the child comments. If I want the serialized resources instead, is there a clean way to get them?Sultanate
use the full=True argument in your declarationEvaporation
Perfect, exactly what I wanted. Thanks!Sultanate
N
1

Can you post your solution?

I have the same use case/models (a foreign key relation ship established in the "child table" pointing back to the parent table) but cannot resolve it.

The solution I have seen is to add this line to your TicketResource a the class level NOT within the meta subclass:

comments = fields.ToManyField('TicketCommentResource', 'ticket', full=True)

Then possibly also add this to TicketCommentResource, again at the class level:

event = fields.ForeignKey('TicketResource', 'event')

But I always get the error that my parent object resource (in your example TicketResource) does not have the attribute 'ticket' which is the second term sent to ToManyField.

I have tried to play with this a lot but can't seem to get the winning combo. The solution you originally posted works but as you yourself pointed out it not ideal.

Thanks!

Niedersachsen answered 17/1, 2013 at 20:53 Comment(1)
Wanted to add this as comment but did not have the option.Niedersachsen

© 2022 - 2024 — McMap. All rights reserved.