跳转到主要内容

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'}]}

同样,您可以根据需要更改关键字startlimit

>>> 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 (15.4 kB 查看哈希值)

上传时间

由以下组织支持