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

巧了我就是萌 提交于 2019-12-09 17:58:53

问题


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()

回答1:


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



回答2:


In RecordsResource class, you need to add model field as well (see https://github.com/tomchristie/django-rest-framework/blob/master/djangorestframework/resources.py#L232-234)

class RecordsResource(BaseRecordResource):
    model = Record

    class Meta(BaseRecordResource.Meta):
        resource_name = 'records'
        queryset = Record.objects.all()



回答3:


Explaining from the beginning:

There are three styles of inheritance that are possible in Django.

  1. Often, you will just want to use the parent class to hold information that you don't want to have to type out for each child model. This class isn't going to ever be used in isolation, so Abstract base classes are what you're after.

  2. If you're subclassing an existing model (perhaps something from another application entirely) and want each model to have its own database table, Multi-table inheritance is the way to go.

  3. Finally, if you only want to modify the Python-level behavior of a model, without changing the models fields in any way, you can use Proxy models.

The choice here is Multi-table inheritance

Multi-table inheritance The second type of model inheritance supported by Django is when each model in the hierarchy is a model all by itself. Each model corresponds to its own database table and can be queried and created individually. The inheritance relationship introduces links between the child model and each of its parents (via an automatically-created OneToOneField) Ref

To go from Record to Recordx where 1 <= x <= n you do a_example_record = Record.objects,get(pk=3) and then check what type of Recordx it is by using something like below

if hasattr(a_example_record, 'record1'):
    # ...
elif hasattr(a_example_record, 'record2'):
    # ...

So now that we know how to get the children from the parent and we need to provide TastyPie with a queryset in its meta, you need to write a custom queryset backed by a custom manager on the Record model that takes all your records (More here Custom QuerySet and Manager without breaking DRY?), checks what type of child it is and appends it to a queryset or a list. You can read about appending here How to combine 2 or more querysets in a Django view?



来源:https://stackoverflow.com/questions/11924132/populating-a-tastypie-resource-for-a-multi-table-inheritance-django-model

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!