问题
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.
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.
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.
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