Django的Ext.Direct实现
项目描述
简介
此软件包提供了一种简单的方法,将Django中的函数/视图公开到包含在Ext.Direct包中的ExtJS 3.0,遵循Ext.Direct规范
请查看docs/INSTALL.txt、tests.py和test_urls.py以查看所需的设置。
我们需要设置__name__变量以稍后访问function.__module__
>>> __name__ = 'extdirect.django.doctest'
让我们创建一个测试浏览器
>>> from django.test.client import Client >>> client = Client()
注册ExtDirect远程提供程序
现在,我们应该能够获取将注册我们的ExtDirect提供程序的provider.js。由于我们尚未注册任何函数,此提供程序的动作将是一个空的配置对象
>>> response = client.get('/remoting/provider.js/') >>> print response.content #doctest: +NORMALIZE_WHITESPACE Ext.onReady(function() { Ext.Direct.addProvider({"url": "/remoting/router/", "type": "remoting", "namespace": "django", "actions": {}}); });
因此,您要注册Ext.RemotingProvider到您的Web应用中要做的只是
<script src="/remoting/provider.js/"></script>
直接访问描述符API
您可能想访问整个ExtDirect远程提供程序的描述符API。在这种情况下,我们可以发出以下请求
>>> response = client.get('/remoting/api/') >>> print response.content #doctest: +NORMALIZE_WHITESPACE Ext.ns('django'); django.Descriptor = {"url": "/remoting/router/", "type": "remoting", "namespace": "django", "actions": {}}
请注意,此响应是javascript代码
>>> print response.__getitem__('content-type') text/javascript
但根据Ext.Direct规范,我们也应该能够以JSON包的形式获取描述符API
>>> response = client.get('/remoting/api/', {'format': 'json'}) >>> print response.content #doctest: +NORMALIZE_WHITESPACE {"url": "/remoting/router/", "type": "remoting", "namespace": "django", "actions": {}, "descriptor": "django.Descriptor"}
只是为了确保
>>> print response.__getitem__('content-type') application/json
使用Ext.direct.RemotingProvider
从现在起,我们将使用_config属性(传递给addProvider函数的配置对象)
>>> from pprint import pprint >>> from extdirect.django import remoting >>> from extdirect.django import tests >>> pprint(tests.remote_provider._config) {'actions': {}, 'namespace': 'django', 'type': 'remoting', 'url': '/remoting/router/'}
好,现在我们将在我们的提供程序实例(tests.remote_provider)上注册一个新函数
>>> @remoting(tests.remote_provider, action='user') ... def list(request): ... pass ...
默认情况下,formHandler将设置为false,len为0,name为函数名
>>> pprint(tests.remote_provider._config) {'actions': {'user': [{'formHandler': False, 'len': 0, 'name': 'list'}]}, 'namespace': 'django', 'type': 'remoting', 'url': '/remoting/router/'}
请注意,ExtDirect有actions(控制器)和methods(方法)。但在这里,我们只有函数。所以,我们使用
@remoting(tests.remote_provider, action='user') def list(request):
来表示,“将list函数添加到user动作”。但这不是必须的,如果我们没有设置action,默认值就是函数的__module__属性(将点‘.’替换为下划线‘_’)。
需要注意的是,您传递给@remoting的签名在服务器端是不相关的。我们暴露给Ext.Direct的函数应该像其他Django视图一样接收请求实例。公开函数的参数将可用在request.extdirect_post_data中(当函数是表单处理器(form_handler=True)时,所有参数也将可在request.POST中找到)。
让我们注册更多函数。
>>> @remoting(tests.remote_provider, action='user', form_handler=True) ... def update(request): ... return dict(success=True, data=[request.POST['username'], request.POST['password']]) ... >>> @remoting(tests.remote_provider, action='posts', len=1) ... def all(request): ... #just return the recieved data ... return dict(success=True, data=request.extdirect_post_data) ... >>> @remoting(tests.remote_provider) ... def module_action(request): ... return dict(success=True)
让我们看看我们提供者的配置对象。
>>> pprint(tests.remote_provider._config) #doctest: +NORMALIZE_WHITESPACE {'actions': {'extdirect_django_doctest': [{'formHandler': False, 'len': 0, 'name': 'module_action'}], 'posts': [{'formHandler': False, 'len': 1, 'name': 'all'}], 'user': [{'formHandler': False, 'len': 0, 'name': 'list'}, {'formHandler': True, 'len': 0, 'name': 'update'}]}, 'namespace': 'django', 'type': 'remoting', 'url': '/remoting/router/'}
是时候进行ExtDirect调用了。在我们的JavaScript中我们将只写
django.posts.all({tag: 'extjs'})
这将转换为POST请求。
>>> from django.utils import simplejson >>> rpc = simplejson.dumps({'action': 'posts', ... 'tid': 1, ... 'method': 'all', ... 'data':[{'tag': 'extjs'}], ... 'type':'rpc'}) >>> response = client.post('/remoting/router/', rpc, 'application/json')
然后我们来检查响应。
>>> pprint(simplejson.loads(response.content)) #doctest: +NORMALIZE_WHITESPACE {u'action': u'posts', u'method': u'all', u'result': {u'data': [{u'tag': u'extjs'}], u'success': True}, u'tid': 1, u'type': u'rpc'}
让我们尝试使用formHandler,你可以看看Ext.Direct表单集成的实时示例。
当我们运行
panelForm.getForm().submit()
Ext.Direct将像这样发起POST请求:
>>> response = client.post('/remoting/router/', ... {'username': 'sancho', ... 'password': 'sancho', ... 'extAction': 'user', ... 'extMethod': 'update', ... 'extUpload': False, ... 'extTID': 2, ... 'extType': 'rpc'})
让我们检查响应。
>>> pprint(simplejson.loads(response.content)) {u'action': u'user', u'isForm': True, u'method': u'update', u'result': {u'data': [u'sancho', u'sancho'], u'success': True}, u'tid': u'2', u'type': u'rpc'}
如果你在ExtJS表单中使用fileUpload,文件将可用在request.FILES中,就像Django处理文件上传一样。
现在,我们将看到异常会发生什么。根据Ext.Direct规范,extdirect.django会检查Django是否在调试模式下运行(settings.DEBUG=True),如果是这样,它将向浏览器返回异常。否则,异常必须在您公开的函数中被捕获。
首先,让我们公开一个会引发异常的函数。
>>> @remoting(tests.remote_provider, action='errors') ... def error(request): ... return "A common mistake" + 1
现在,我们模拟在调试模式下的执行。
>>> from django.conf import settings >>> settings.DEBUG = True >>> rpc = simplejson.dumps({'action': 'errors', ... 'tid': 1, ... 'method': 'error', ... 'data':[], ... 'type':'rpc'}) >>> response = client.post('/remoting/router/', rpc, 'application/json') >>> pprint(simplejson.loads(response.content)) {u'action': u'errors', u'message': u"TypeError: cannot concatenate 'str' and 'int' objects\n", u'method': u'error', u'tid': 1, u'type': u'exception', u'where': [u'<doctest ...>', 3, u'error', u'return "A common mistake" + 1']}
请注意,在where属性中,您将得到[文件名、行号、函数、语句],以便在调试时帮助您。
让我们看看如果我们关闭调试模式会发生什么。
>>> settings.DEBUG = False >>> response = client.post('/remoting/router/', rpc, 'application/json') #doctest: +NORMALIZE_WHITESPACE Traceback (most recent call last): ... TypeError: cannot concatenate 'str' and 'int' objects
引发的异常必须在服务器上被捕获,浏览器对此一无所知。
注册ExtDirect轮询提供者。
就像我们之前对ExtDirect Remoting提供者所做的那样。
>>> response = client.get('/polling/provider.js/') >>> print response.content #doctest: +NORMALIZE_WHITESPACE Ext.onReady(function() { Ext.Direct.addProvider({"url": "/polling/router/", "type": "polling"}); });
因此,您要注册Ext.PollingProvider到您的Web应用程序中,只需要做以下操作:
<script src="/polling/provider.js/"></script>
使用Ext.direct.PollingProvider。
在本节中,我们将展示如何使用Ext.direct.PollingProvider。Ext.direct.PollingProvider提供重复轮询服务器在不同间隔(默认为3000 - 每3秒)的功能。
因为我们没有为轮询提供者设置函数,如果我们调用它,我们应该得到一个异常。
>>> response = client.get('/polling/router/') #doctest: +NORMALIZE_WHITESPACE Traceback (most recent call last): ... RuntimeError: The server provider didn't register a function to run yet
但是,与ExtRemotingProvider一样,当Django在调试模式下时,异常会被返回到浏览器。
>>> settings.DEBUG = True >>> response = client.get('/polling/router/') >>> pprint(simplejson.loads(response.content)) #doctest: +NORMALIZE_WHITESPACE {u'message': u"RuntimeError: The server provider didn't register a function to run yet\n", u'type': u'exception', u'where': [u'...', 311, u'router', u'raise RuntimeError("The server provider didn\'t register a function to run yet")']} >>> settings.DEBUG = False
所以,让我们声明一个简单的函数并将其分配给我们的轮询提供者。
>>> from extdirect.django import polling >>> @polling(tests.polling_provider) ... def my_polling(request): ... return "I'm tired..." >>> response = client.get('/polling/router/') >>> pprint(simplejson.loads(response.content)) #doctest: +NORMALIZE_WHITESPACE {u'data': u"I'm tired...", u'name': u'some-event', u'type': u'event'}
使用ExtDirectStore辅助类。
ExtDirectStore是一个辅助类,您可能希望在使用ExtJS时使用它来加载给定的Ext.data.DirectStore。
请注意,您应该使用len=1(Python)和paramsAsHash=true(JavaScript)以确保一切正常工作。
让我们看看最简单的用例。
>>> from extdirect.django import ExtDirectStore >>> from extdirect.django.models import ExtDirectStoreModel >>> list = ExtDirectStore(ExtDirectStoreModel) >>> pprint(list.query()) #doctest: +NORMALIZE_WHITESPACE {'records': [{'id': 1, 'name': u'Homer'}, {'id': 2, 'name': u'Joe'}], 'total': 2}
所以一个快速而几乎完整的例子可以是:
在Django中:
@remoting(provider, action='user', len=1) def load_users(request): data = request.extdirect_post_data[0] users = ExtDirectStore(User) return users.query(**data)
在ExtJS中:
new Ext.data.DirectStore({ paramsAsHash: true, directFn: django.user.load_users, fields: [ {name: 'first_name'}, {name: 'last_name'}, {name: 'id'} ], // defaults in django root: 'records', idProperty: 'id', totalProperty: 'total', ... })
如上例所示,您可能希望向方法query传递关键字参数以过滤查询。
>>> pprint(list.query(id=1)) {'records': [{'id': 1, 'name': u'Homer'}], 'total': 1}
您可以在创建时更改(或设置)ExtDirectStore使用的关键字。
>>> list.root = 'users' >>> list.total = 'result' >>> pprint(list.query()) {'result': 2, 'users': [{'id': 1, 'name': u'Homer'}, {'id': 2, 'name': u'Joe'}]}
如果您使用分页,ExtDirectStore会负责。
>>> pprint(list.query(start=0, limit=2)) {'result': 2, 'users': [{'id': 1, 'name': u'Homer'}, {'id': 2, 'name': u'Joe'}]} >>> pprint(list.query(start=0, limit=1)) {'result': 2, 'users': [{'id': 1, 'name': u'Homer'}]} >>> pprint(list.query(start=1, limit=1)) {'result': 2, 'users': [{'id': 2, 'name': u'Joe'}]}
同样,您可以根据需要更改关键字start和limit。
>>> list.start = 'from' >>> list.limit = 'to' >>> kw = {'from':0, 'to':1} >>> pprint(list.query(**kw)) {'result': 2, 'users': [{'id': 1, 'name': u'Homer'}]}
排序也包括在内。
>>> pprint(list.query(sort='name', dir='ASC')) {'result': 2, 'users': [{'id': 1, 'name': u'Homer'}, {'id': 2, 'name': u'Joe'}]} >>> pprint(list.query(sort='name', dir='DESC')) {'result': 2, 'users': [{'id': 2, 'name': u'Joe'}, {'id': 1, 'name': u'Homer'}]}
而且...您也可以更改这些关键字。
>>> list.sort = 'sort_field' >>> list.dir = 'sort_order' >>> pprint(list.query(sort_field='name', sort_order='ASC')) {'result': 2, 'users': [{'id': 1, 'name': u'Homer'}, {'id': 2, 'name': u'Joe'}]} >>> pprint(list.query(sort_field='name', sort_order='DESC')) {'result': 2, 'users': [{'id': 2, 'name': u'Joe'}, {'id': 1, 'name': u'Homer'}]}
最后,有时您需要运行复杂的查询。我们为此提供了两种选择。首先,您可以为 ExtDirectStore 传递或设置一个 extras 参数。这应该是一个类似于以下列表的元组列表:
>>> def name_size(rec): ... return len(rec.name) >>> >>> extras = [('name_size', name_size),('name_upper', lambda rec: rec.name.upper())] >>> list.extras = extras >>> pprint(list.query()) #doctest: +NORMALIZE_WHITESPACE {'result': 2, 'users': [{'id': 1, 'name': u'Homer', 'name_size': 5, 'name_upper': u'HOMER'}, {'id': 2, 'name': u'Joe', 'name_size': 3, 'name_upper': u'JOE'}]} >>> list.extras = []
- extras 列表中的每个项目都应该是一个包含以下内容的元组:
属性名
可调用对象(仅接受一个必需参数)
每个元组中的可调用对象将为 queryset 中的每个对象执行,以获取该属性的值。
运行复杂查询的第二种方法非常简单。
>>> qs = ExtDirectStoreModel.objects.exclude(id=2) >>> pprint(list.query(qs)) {'result': 1, 'users': [{'id': 1, 'name': u'Homer'}]}
在这里,我们只需将一个有效的 queryset 传递给 query 函数。使用这个 queryset,ExtDirectStore 将应用我们之前看到的所有内容(过滤、分页、排序)。您可以使用所有 Django ORM 功能创建一个复杂的 queryset,然后将其传递给 query 方法。
最后,让我们看看当您在模型中定义外键时会发生什么。
>>> from extdirect.django.models import Model >>> ds = ExtDirectStore(Model) >>> pprint(ds.query()) {'records': [{'fk_model': 1, 'fk_model_id': 1, 'id': 1}], 'total': 1}
- 对于每个外键字段(fk_model),您将获得两个具有相同值的属性:
fk_model
fk_model_id
变更
0.3 (2009-10-15)
错误修复:ExtDirectStore 返回错误的 'total' 参数
添加了对描述符 API 的直接访问,带有可选参数 format=json,以获取作为 JavaScript 代码或 JSON 数据包的描述符
0.2 (2009-09-11)
错误的 setup.py
0.1 (2009-09-11)
第一个公开版本
项目详情
extdirect.django-0.3.tar.gz 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 2883912bba37a618e1d3410101623ac05837f86b8170e3a0ededf8766e109044 |
|
MD5 | bf0e9c1eac063bd2fb8db0c1b4059abc |
|
BLAKE2b-256 | 71b735f46eb2cd6c164446e2f4e3577bcc18eb077aa9451dd713cfea9f9b6047 |