Populating a tastypie resource for a multi-table inheritance Django model

django, django-models, tastypie

Solution

Ok, I just solved it. I've simplified the code.

Given the following code...

`models.py`

from django.db import models
from model_utils.managers import InheritanceManager


class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

    # https://github.com/carljm/django-model-utils#inheritancemanager
    objects = InheritanceManager()


class Restaurant(Place):
    custom_field = models.BooleanField()


class Bar(Place):
    custom_field = models.BooleanField()

`api.py`

from core.models import Place, Restaurant, Bar
# http://django-tastypie.readthedocs.org/en/latest/cookbook.html#pretty-printed-json-serialization
from core.utils import PrettyJSONSerializer
from tastypie.resources import ModelResource


class PlaceResource(ModelResource):
    class Meta:
        queryset = Place.objects.select_subclasses()
        resource_name = 'place'
        serializer = PrettyJSONSerializer()


class RestaurantResource(ModelResource):
    class Meta:
        queryset = Restaurant.objects.all()
        resource_name = 'restaurant'
        serializer = PrettyJSONSerializer()


class BarResource(ModelResource):
    class Meta:
        queryset = Bar.objects.all()
        resource_name = 'bar'
        serializer = PrettyJSONSerializer()

Output

`http://localhost:8000/api/v1/bar/?format=json`

{
  "meta": {
    "limit": 20,
    "next": null,
    "offset": 0,
    "previous": null,
    "total_count": 1
  },
  "objects": [
    {
      "address": "dawdaw",
      "custom_field": true,
      "id": "1",
      "name": "dwdwad",
      "resource_uri": "/api/v1/bar/1/"
    }
  ]
}

OK

`http://localhost:8000/api/v1/restaurant/?format=json`

{
  "meta": {
    "limit": 20,
    "next": null,
    "offset": 0,
    "previous": null,
    "total_count": 1
  },
  "objects": [
    {
      "address": "nhnhnh",
      "custom_field": true,
      "id": "2",
      "name": "nhnhnh",
      "resource_uri": "/api/v1/restaurant/2/"
    }
  ]
}

OK

`http://localhost:8000/api/v1/place/?format=json`

{
  "meta": {
    "limit": 20,
    "next": null,
    "offset": 0,
    "previous": null,
    "total_count": 2
  },
  "objects": [
    {
      "address": "dawdaw",
      "id": "1",
      "name": "dwdwad",
      "resource_uri": "/api/v1/place/1/"
    },
    {
      "address": "nhnhnh",
      "id": "2",
      "name": "nhnhnh",
      "resource_uri": "/api/v1/place/2/"
    }
  ]
}

What I want to achieve

{
  "meta": {
    "limit": 20,
    "next": null,
    "offset": 0,
    "previous": null,
    "total_count": 2
  },
  "objects": [
    {
      "address": "dawdaw",
      "custom_field": true,
      "id": "1",
      "name": "dwdwad",
      "resource_uri": "/api/v1/bar/1/"
    },
    {
      "address": "nhnhnh",
      "custom_field": true,
      "id": "2",
      "name": "nhnhnh",
      "resource_uri": "/api/v1/restaurant/2/"
    }
  ]
}

Solution:

from core.models import Place, Restaurant, Bar
# http://django-tastypie.readthedocs.org/en/latest/cookbook.html#pretty-printed-json-serialization
from core.utils import PrettyJSONSerializer
from tastypie.resources import ModelResource

class RestaurantResource(ModelResource):
    class Meta:
        queryset = Restaurant.objects.all()
        resource_name = 'restaurant'
        serializer = PrettyJSONSerializer()


class BarResource(ModelResource):
    class Meta:
        queryset = Bar.objects.all()
        resource_name = 'bar'
        serializer = PrettyJSONSerializer()

class PlaceResource(ModelResource):
    class Meta:
        queryset = Place.objects.select_subclasses()
        resource_name = 'place'
        serializer = PrettyJSONSerializer()

    def dehydrate(self, bundle):
        # bundle.data['custom_field'] = "Whatever you want"
        if isinstance(bundle.obj, Restaurant):
            restaurant_res = RestaurantResource()
            rr_bundle = restaurant_res.build_bundle(obj=bundle.obj, request=bundle.request)
            bundle.data = restaurant_res.full_dehydrate(rr_bundle).data
        elif isinstance(bundle.obj, Bar):
            bar_res = BarResource()
            br_bundle = bar_res.build_bundle(obj=bundle.obj, request=bundle.request)
            bundle.data = bar_res.full_dehydrate(br_bundle).data
        return bundle

Problem

Given the following code I was wondering how to populate `RecordsResource` with each real record data: `models.py` ``` class Record(models.Model): content_type = models.ForeignKey(ContentType, editable=False, null=True) user = models.ForeignKey(User, related_name='records') issued = models.DateTimeField(auto_now_add=True) date = models.DateField() def save(self, *args, **kwargs): if not self.content_type: self.content_type = ContentType.objects.get_for_model(self.__class__) super(Record, self).save(*args, **kwargs) def as_leaf_class(self): model = self.content_type.model_class() if model == self.__class__: return self return model.objects.get(pk=self.id) class Record1(Record): # some fields # ... class RecordN(Record): # some fields ``` `api.py` ``` class BaseModelResource(ModelResource): class Meta(object): authentication = ApiKeyPlusWebAuthentication() authorization= Authorization() cache = SimpleCache() throttle = CacheDBThrottle( throttle_at=350, # 1 day expiration=86400 ) if settings.DEBUG: serializer = PrettyJSONSerializer() def obj_create(self, bundle, request=None, **kwargs): return super(BaseModelResource, self).obj_create(bundle, request, user=request.user) def apply_authorization_limits(self, request, object_list): return object_list.filter(user=request.user) class BaseRecordResource(BaseModelResource): class Meta(BaseModelResource.Meta): filtering = { 'date': ALL } excludes = ['issued'] class RecordsResource(BaseRecordResource): class Meta(BaseRecordResource.Meta): resource_name = 'records' queryset = Record.objects.all() class Record1Resource(BaseRecordResource): class Meta(BaseRecordResource.Meta): resource_name = 'record1' queryset = Record1.objects.all() # ... class RecordNResource(BaseRecordResource): class Meta(BaseRecordResource.Meta): resource_name = 'recordn' queryset = RecordN.objects.all() ```

Original source

Related problems