我尝试使用抽象模型来实现它,然后通过ModelResource
. 我不得不为此重写obj_create
和get_resource_uri
方法。
请看一下示例代码(我已经在我的本地机器上测试过)。
楷模
from django.db import models
from django.contrib.auth.models import User
class UserGame(models.Model):
user = models.ForeignKey(User)
# more fields like name etc.
name = models.CharField("name", max_length=40)
def __unicode__(self):
return self.name
class UserHost(models.Model):
game = models.ForeignKey(UserGame)
# more fields like hostname etc.
hostname = models.CharField("host name", max_length=40)
def __unicode__(self):
return self.hostname
class JoinGame(models.Model):
game = models.ForeignKey(UserGame)
host = models.ForeignKey(UserHost)
class Meta:
abstract = True
使用 Tastypie 的 API 资源
from django.contrib.auth.models import User
from tastypie import fields
from tastypie.authentication import (BasicAuthentication, ApiKeyAuthentication,
MultiAuthentication)
from tastypie.authorization import Authorization
from tastypie.resources import Resource, ModelResource
from tastypie.exceptions import BadRequest
from .models import UserGame, UserHost, JoinGame
class UserResource(ModelResource):
class Meta:
resource_name = "user"
# For authentication, allow both basic and api key so that the key
# can be grabbed, if needed.
authentication = MultiAuthentication(BasicAuthentication(),
ApiKeyAuthentication())
authorization = Authorization()
# Because this can be updated nested under the other Resources.
# It needs put/patch to be able to handle partial updates.
allowed_methods = ['get', 'patch', 'put', ]
always_return_data = True
queryset = User.objects.all().select_related("api_key")
excludes = ['is_active', 'is_staff', 'is_superuser', 'date_joined',
'last_login', 'password']
class UserGameResource(ModelResource):
user = fields.ForeignKey(UserResource, 'user', full=True)
class Meta:
resource_name = "user_game"
authentication = ApiKeyAuthentication()
authorization = Authorization()
# Because this can be updated nested under the other Resources.
# It needs put/patch to be able to handle partial updates.
allowed_methods = ['get', 'post', 'patch', 'put']
always_return_data = True
queryset = UserGame.objects.all()
class UserHostResource(ModelResource):
resource_name = "user_host"
user_game = fields.ForeignKey(UserGameResource, 'game', full=True)
class Meta:
authentication = ApiKeyAuthentication()
authorization = Authorization()
# Because this can be updated nested under the other Resources.
# It needs put/patch to be able to handle partial updates.
allowed_methods = ['get', 'post', 'patch', 'put']
always_return_data = True
queryset = UserHost.objects.all()
加入游戏资源
class JoinGameResource(Resource):
game = fields.ForeignKey(UserGameResource, "game", null=True, full=True)
host = fields.ForeignKey(UserHostResource, "host", null=True, full=True)
class Meta:
resource_name = "join_game"
object_class = JoinGame
authentication = ApiKeyAuthentication()
authorization = Authorization()
# Because this can be updated nested under the other Resources.
# It needs put/patch to be able to handle partial updates.
list_allowed_methods = ['post']
detail_allowed_methods = []
fields = ["game", "host"]
always_return_data = True
def hydrate(self, bundle):
if "game" not in bundle.data:
raise BadRequest("Must provide 'game' when joining a game.")
return bundle
def obj_create(self, bundle, **kwargs):
try:
game = bundle.data.get("game")
game_bundle = self.build_bundle(obj=UserGame(), data=game,
request=bundle.request)
updated_game_bundle = UserGameResource().obj_create(
game_bundle, **kwargs)
bundle.obj.game = updated_game_bundle.obj
except KeyError:
raise BadRequest("Must provide 'game' when joining a game.")
try:
host = bundle.data.get("host")
host_bundle = self.build_bundle(obj=UserHost(), data=host,
request=bundle.request)
updated_host_bundle = UserHostResource().obj_create(
host_bundle, game=updated_game_bundle.obj, **kwargs)
bundle.obj.host = updated_host_bundle.obj
except KeyError:
pass
#raise BadRequest("Must provide 'host' when joining a host.")
return bundle
def get_resource_uri(self, updated_bundle):
# Since JoinGame is abstract model, pk won't be available
# So, get_resource_uri method of ModelResource will raise exception
# Let's override that
return None
网址
from .api import (
UserResource, UserGameResource, UserHostResource, JoinGameResource)
from django.conf.urls import patterns, include
from tastypie.api import Api
# Create an empty pattern so that we can just build it using +=
urlpatterns = patterns(None)
#==============================================================================
# API Resources
#==============================================================================
v1_api = Api(api_name='v1')
v1_api.register(UserResource())
v1_api.register(UserGameResource())
v1_api.register(UserHostResource())
v1_api.register(JoinGameResource())
urlpatterns += patterns(
'',
(r'^api/', include(v1_api.urls)),
)
示例请求正文
{
"game": {
"name": "New Game",
"user": "/api/v1/user/1/"
},
"host": {
"hostname": "New Host name"
}
}
示例响应正文
{
"game": {
"id": 15,
"name": "New Game",
"resource_uri": "/api/v1/user_game/15/",
"user": {
"email": "psjinx@example.com",
"first_name": "",
"id": 1,
"last_name": "",
"resource_uri": "/api/v1/user/1/",
"username": "psjinx"
}
},
"host": {
"hostname": "New Host name",
"id": 13,
"resource_uri": "/api/v1/userhost/13/",
"user_game": {
"id": 15,
"name": "New Game",
"resource_uri": "/api/v1/user_game/15/",
"user": {
"email": "psjinx@example.com",
"first_name": "",
"id": 1,
"last_name": "",
"resource_uri": "/api/v1/user/1/",
"username": "psjinx"
}
}
},
"resource_uri": null
}