How do I make Django-Piston to include related child objects in the serialized output?
Asked Answered
D

2

11

I am pulling my hair out here because this isn't working for me and seems like it should be.

I am using Django-Piston to develop an API and have 2 models, Building and Building Area.

BuildingArea has a ForeignKey to Building as there are multiple areas in a building. The 'related_name' property for the FK is 'areas' so I can access the BuildingAreas from a given Building.

The problem is that it all looks fine in Admin but when I hit the /api/building.json endpoint, all I get it the Building object without the nested BuildingArea objects included in the JSON.

I would have thought that Django-Piston would follow reverse FK fields by default or am I missing something?

handlers.py

class BuildingHandler(BaseHandler):

    allowed_methods = ('GET',)    
    model = Building

    def read(self, name=None):
        return self.model.objects.all()

models.py

class Building(models.Model):
    address         = models.CharField(max_length=255)

    def __unicode__(self):
        return self.address 

class BuildingArea(models.Model):
    display_name  = models.CharField(max_length=30)
    building      = models.ForeignKey(Building, related_name='areas') 

    def __unicode__(self):
        return self.display_name 
Devoirs answered 21/2, 2010 at 12:43 Comment(0)
D
19

Ok so I got it working finally after debugging thru emitters.py and noting how it uses the 'fields' property of the handler to iterate the Model fields.

These are my models:

class Building(models.Model):
    address         = models.CharField(max_length=255)

    def __unicode__(self):
        return self.address 

class BuildingArea(models.Model):
    display_name  = models.CharField(max_length=30)
    building      = models.ForeignKey(Building, related_name='areas') 

    def __unicode__(self):
        return self.display_name 

This is what my BuildingHandler looks like now:

class BuildingHandler(BaseHandler):

    allowed_methods = ('GET',)    
    fields = ('address', ('areas', ('display_name',),),)    
    model = Building

    def read(self, name=None):
        return self.model.objects.all()

The important thing to note here is that emmitters.py will activate certain codepaths only if the current field definition is a set or a list. I had forgotten to add a trailing ',' to the sets used to define the fields and this caused Piston to cause Python to return a set made of the characters contained in the string, 'display_name', rather than a set containing the string 'display_name'. I hope that made sense, Google 'Python single set trailing comma' for more info.

Hopefully this helps someone else! :D

Devoirs answered 25/2, 2010 at 6:26 Comment(4)
TEN THOUSAND upvotes to you sir (or madam). Or at least one. Somehow I musta missed that. Piston continues to surprise me.Reticent
As crazy as I thought this answer was, this is the correct one. +1 and bookmarked for when I forget.Silvana
Now that saved me a lot of time.Tokharian
By "set" I believe you mean "tuple".Multiflorous
G
0

On BuildingHandler, do:

fields = ('address', 'areas')

That should do it.

Gynaecocracy answered 21/2, 2010 at 12:59 Comment(2)
Ok I must have stuffed something up because I thought I had already tried that. I'll try it and report back.Devoirs
Ok I tried that and it is still not working - output is: [ { "areas": "", "address": "42 Frigate Crescent" } ] But there is definitely a BuildingArea in the db with its relationship set up correctly to point to Building.Devoirs

© 2022 - 2024 — McMap. All rights reserved.