Django构建RESTful接口的简单性
我最近有一个新项目,想找个理由学习Django。通常我喜欢建立RESTful的服务器接口,也就是通过网址来获取一些资源,然后返回一些平台无关的数据,比如XML或JSON。这其实不需要框架也能做到,但像Ruby on Rails这样的框架可以很方便地根据你传入的网址类型,自动返回XML数据,这样就省了很多麻烦。
我想问的是,Django有没有类似的支持?我在网上查了一下,发现有一些可以在Django上使用的第三方“RESTful”代码,但我对这些不是很感兴趣。
如果Django不行,有没有其他的Python框架是专门为这个目的设计的?这样我就不用像在PHP中那样重新发明轮子了。
6 个回答
看看Piston吧,它是一个为Django设计的小框架,用来创建RESTful API。
最近,Eric Holscher在他的博客上写了一篇文章,详细介绍了使用Piston的一些好处:Django中的大问题,基本解决:API
(我不得不删掉一些明显的链接。)
给 piston
点个赞 - (上面的链接)。我之前用过 apibuilder
(华盛顿时报的开源项目),但 Piston 对我来说更简单。对我来说最难的就是搞清楚我的API的URL结构,以及如何使用正则表达式。还有,我也用过 surlex,它让这项工作变得简单多了。
举个例子,使用这个 Group
模型(来自我们正在开发的时间表系统):
class Group(models.Model):
"""
Tree-like structure that holds groups that may have other groups as leaves.
For example ``st01gp01`` is part of ``stage1``.
This allows subgroups to work. The name is ``parents``, i.e.::
>>> stage1group01 = Group.objects.get(unique_name = 'St 1 Gp01')
>>> stage1group01
>>> <Group: St 1 Gp01>
# get the parents...
>>> stage1group01.parents.all()
>>> [<Group: Stage 1>]
``symmetrical`` on ``subgroup`` is needed to allow the 'parents' attribute to be 'visible'.
"""
subgroup = models.ManyToManyField("Group", related_name = "parents", symmetrical= False, blank=True)
unique_name = models.CharField(max_length=255)
name = models.CharField(max_length=255)
academic_year = models.CharField(max_length=255)
dept_id = models.CharField(max_length=255)
class Meta:
db_table = u'timetable_group'
def __unicode__(self):
return "%s" % self.name
还有这个 urls.py 的片段(注意 surlex 让正则表达式宏的设置变得很简单):
from surlex.dj import surl
from surlex import register_macro
from piston.resource import Resource
from api.handlers import GroupHandler
group_handler = Resource(GroupHandler)
# add another macro to our 'surl' function
# this picks up our module definitions
register_macro('t', r'[\w\W ,-]+')
urlpatterns = patterns('',
# group handler
# all groups
url(r'^groups/$', group_handler),
surl(r'^group/<id:#>/$', group_handler),
surl(r'^group/<name:t>/$', group_handler),)
然后这个处理器默认会处理 JSON 输出,也可以处理 XML 和 YAML。
class GroupHandler(BaseHandler):
"""
Entry point for Group model
"""
allowed_methods = ('GET', )
model = Group
fields = ('id', 'unique_name', 'name', 'dept_id', 'academic_year', 'subgroup')
def read(self, request, id=None, name=None):
base = Group.objects
if id:
print self.__class__, 'ID'
try:
return base.get(id=id)
except ObjectDoesNotExist:
return rc.NOT_FOUND
except MultipleObjectsReturned: # Should never happen, since we're using a primary key.
return rc.BAD_REQUEST
else:
if name:
print self.__class__, 'Name'
return base.filter(unique_name = name).all()
else:
print self.__class__, 'NO ID'
return base.all()
如你所见,大部分处理器的代码都是在搞清楚 urlpatterns
中传递了哪些参数。
一些示例 URL 是 api/groups/
、api/group/3301/
和 api/group/st1gp01/
- 这些都会输出 JSON。
这件事可能很简单。
URL映射很容易构建,比如:
urlpatterns = patterns('books.views',
(r'^books/$', 'index'),
(r'^books/(\d+)/$', 'get'))
Django支持模型序列化,所以把模型转换成XML也很简单:
from django.core import serializers
from models import Book
data = serializers.serialize("xml", Book.objects.all())
把这两者结合起来,再加上装饰器,你就可以快速构建处理程序:
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
def xml_view(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return HttpResponse(serializers.serialize("xml", result),
mimetype="text/xml")
return wrapper
@xml_view
def index(request):
return Books.objects.all()
@xml_view
def get(request, id):
return get_object_or_404(Book, pk=id)