{
"info": {
"author": "Santiago Videla",
"author_email": "santiago.videla@gmail.com",
"bugtrack_url": null,
"classifiers": [
"Framework :: Django",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Topic :: Software Development :: Libraries :: Python Modules"
],
"description": "Introduction\n============\n\nThis package provides a simple way to expose functions/views in django to the\n`Ext.Direct`_ package included in `ExtJS 3.0`_ following the `Ext.Direct specification`_\n\n.. _`ExtJS 3.0`: http://www.extjs.com/\n.. _`Ext.Direct`: http://extjs.com/blog/2009/05/13/introducing-ext-direct/\n.. _`Ext.Direct specification`: http://extjs.com/products/extjs/direct.php\n \n\nTake a look to docs/INSTALL.txt, tests.py and test_urls.py to see the needed setup.\n\nWe need to set the __name__ variable to access to function.__module__ later::\n\n >>> __name__ = 'extdirect.django.doctest'\n\nLet's create a test browser::\n\n >>> from django.test.client import Client\n >>> client = Client()\n\nRegister the ExtDirect remoting provider\n----------------------------------------\n\nNow, we should be able to get the `provider.js` that will register\nour ExtDirect provider. As we didn't register any function yet, the `actions`\nfor this provider will be an empty config object ::\n \n >>> response = client.get('/remoting/provider.js/')\n >>> print response.content #doctest: +NORMALIZE_WHITESPACE \n Ext.onReady(function() {\n Ext.Direct.addProvider({\"url\": \"/remoting/router/\",\n \"type\": \"remoting\",\n \"namespace\": \"django\",\n \"actions\": {}});\n });\n \nSo, all you have to do to register the Ext.RemotingProvider in your web application is::\n\n \n \nDirect access to the descriptor API\n-----------------------------------\n\nYou may want to access to the whole descriptor API of your ExtDirect Remoting\nprovider. In that case, we could make a request as follows::\n\n >>> response = client.get('/remoting/api/')\n >>> print response.content #doctest: +NORMALIZE_WHITESPACE\n Ext.ns('django');\n django.Descriptor = {\"url\": \"/remoting/router/\",\n \"type\": \"remoting\",\n \"namespace\": \"django\",\n \"actions\": {}}\n\nNote that this response it's javascript code::\n\n >>> print response.__getitem__('content-type')\n text/javascript\n \nBut according to the Ext.Direct specification, we should also\nbe able to get the descriptor API as a JSON packet::\n\n >>> response = client.get('/remoting/api/', {'format': 'json'})\n >>> print response.content #doctest: +NORMALIZE_WHITESPACE\n {\"url\": \"/remoting/router/\",\n \"type\": \"remoting\",\n \"namespace\": \"django\",\n \"actions\": {},\n \"descriptor\": \"django.Descriptor\"}\n \nAnd just to be sure::\n\n >>> print response.__getitem__('content-type')\n application/json\n \nUsing Ext.direct.RemotingProvider\n---------------------------------\n \nWe will use the `_config` property from now on, (the config object passed to\n`addProvider` function)::\n \n >>> from pprint import pprint\n >>> from extdirect.django import remoting\n >>> from extdirect.django import tests\n \n >>> pprint(tests.remote_provider._config)\n {'actions': {},\n 'namespace': 'django',\n 'type': 'remoting',\n 'url': '/remoting/router/'}\n \nOk, now we are going to register a new function on our provider instance (`tests.remote_provider`)::\n\n >>> @remoting(tests.remote_provider, action='user')\n ... def list(request):\n ... pass\n ...\n\nBy default, `formHandler` will be set to false, `len` to 0 and `name` to the function name::\n\n >>> pprint(tests.remote_provider._config)\n {'actions': {'user': [{'formHandler': False, 'len': 0, 'name': 'list'}]},\n 'namespace': 'django',\n 'type': 'remoting',\n 'url': '/remoting/router/'}\n\nNote that ExtDirect has `actions` (controllers) and `methods`. But here, we have just functions.\nSo, we use::\n\n @remoting(tests.remote_provider, action='user')\n def list(request):\n \nto say, \"add the `list` function to the `user` action\".\nBut this is optional, if we don't set the `action`, the default value it's the function __module__\nattribute (replacing '.' with '_')\n\nIt's important to note, that the signature that you pass to `@remoting` it's not\nrelevant in the server-side. The functions that we expose to Ext.Direct should\nreceive just the `request` instace like any other django view. The parameters for\nthe exposed function, will be available in `request.extdirect_post_data` (when\nthe function it's a form handler (form_handler=True), all the parameters will be\nalso available in `request.POST`).\n\nLet's register a few more functions::\n\n >>> @remoting(tests.remote_provider, action='user', form_handler=True)\n ... def update(request): \n ... return dict(success=True, data=[request.POST['username'], request.POST['password']])\n ...\n >>> @remoting(tests.remote_provider, action='posts', len=1)\n ... def all(request):\n ... #just return the recieved data\n ... return dict(success=True, data=request.extdirect_post_data)\n ...\n >>> @remoting(tests.remote_provider)\n ... def module_action(request): \n ... return dict(success=True) \n \nLet's take a look to the config object for our provider::\n \n >>> pprint(tests.remote_provider._config) #doctest: +NORMALIZE_WHITESPACE\n {'actions': {'extdirect_django_doctest': [{'formHandler': False, 'len': 0, 'name': 'module_action'}],\n 'posts': [{'formHandler': False, 'len': 1, 'name': 'all'}],\n 'user': [{'formHandler': False, 'len': 0, 'name': 'list'},\n {'formHandler': True, 'len': 0, 'name': 'update'}]},\n 'namespace': 'django',\n 'type': 'remoting',\n 'url': '/remoting/router/'}\n\nIt's time to make an ExtDirect call. In our javascript we will write just::\n\n django.posts.all({tag: 'extjs'})\n \nThis will be converted to a POST request::\n\n >>> from django.utils import simplejson\n >>> rpc = simplejson.dumps({'action': 'posts',\n ... 'tid': 1,\n ... 'method': 'all',\n ... 'data':[{'tag': 'extjs'}],\n ... 'type':'rpc'})\n >>> response = client.post('/remoting/router/', rpc, 'application/json')\n\nAnd let's check the reponse::\n \n >>> pprint(simplejson.loads(response.content)) #doctest: +NORMALIZE_WHITESPACE\n {u'action': u'posts',\n u'method': u'all',\n u'result': {u'data': [{u'tag': u'extjs'}], u'success': True},\n u'tid': 1,\n u'type': u'rpc'}\n \nLet's try with a formHandler, you may want to see the `Ext.Direct Form Integration`_\nfor a live example.\n\n.. _`Ext.Direct Form Integration`: http://extjs.com/deploy/dev/examples/direct/direct-form.php\n\nWhen we run::\n\n panelForm.getForm().submit()\n \nExt.Direct will make a POST request like this::\n\n >>> response = client.post('/remoting/router/',\n ... {'username': 'sancho',\n ... 'password': 'sancho',\n ... 'extAction': 'user',\n ... 'extMethod': 'update',\n ... 'extUpload': False,\n ... 'extTID': 2,\n ... 'extType': 'rpc'})\n\nLet's check the reponse::\n\n >>> pprint(simplejson.loads(response.content))\n {u'action': u'user',\n u'isForm': True,\n u'method': u'update',\n u'result': {u'data': [u'sancho', u'sancho'], u'success': True},\n u'tid': u'2',\n u'type': u'rpc'}\n \nIf you use `fileUpload`_ in your ExtJS form, the files will be available in\n`request.FILES`, just as Django handles the `File Uploads`_.\n\n.. _`fileUpload`: http://www.extjs.com/deploy/dev/docs/?class=Ext.form.BasicForm#Ext.form.BasicForm-fileUpload\n.. _`File Uploads`: http://docs.djangoproject.com/en/dev/topics/http/file-uploads/\n\nNow, we are going to see what happen with exceptions. Following the Ext.Direct specification\nextdirect.django will check if django it's running on debug mode (settings.DEBUG=True) and in\nthat case, it will return the exception to the browser. Otherwise, the exceptions must be\ncatched by the function that you expose.\n\nFirst, let's expose a function that raise an Exception::\n\n >>> @remoting(tests.remote_provider, action='errors')\n ... def error(request): \n ... return \"A common mistake\" + 1\n \nAnd now, we simulate the execution on debug mode::\n\n >>> from django.conf import settings\n >>> settings.DEBUG = True\n\n >>> rpc = simplejson.dumps({'action': 'errors',\n ... 'tid': 1,\n ... 'method': 'error',\n ... 'data':[],\n ... 'type':'rpc'})\n >>> response = client.post('/remoting/router/', rpc, 'application/json')\n >>> pprint(simplejson.loads(response.content))\n {u'action': u'errors',\n u'message': u\"TypeError: cannot concatenate 'str' and 'int' objects\\n\",\n u'method': u'error',\n u'tid': 1,\n u'type': u'exception',\n u'where': [u'',\n 3,\n u'error',\n u'return \"A common mistake\" + 1']}\n\nNote that in the `where` attribute, you will have [filename, lineno, function, statment] in order to\nhelp you at debugging time.\n\nLet's see what happen if we turn off the debug mode::\n\n >>> settings.DEBUG = False\n >>> response = client.post('/remoting/router/', rpc, 'application/json') #doctest: +NORMALIZE_WHITESPACE \n Traceback (most recent call last):\n ...\n TypeError: cannot concatenate 'str' and 'int' objects \n \nThe exception raised must be catched in the server and the browser doesn't know anything about it.\n\nRegister the ExtDirect polling provider\n---------------------------------------\n\nAs we did above with the ExtDirect Remoting provider::\n\n >>> response = client.get('/polling/provider.js/')\n >>> print response.content #doctest: +NORMALIZE_WHITESPACE\n Ext.onReady(function() {\n Ext.Direct.addProvider({\"url\": \"/polling/router/\", \"type\": \"polling\"});\n });\n \nSo, all you have to do to register the Ext.PollingProvider in your web application is::\n\n \n\nUsing Ext.direct.PollingProvider\n--------------------------------\n\nIn this section we are going to show how you can use the Ext.direct.PollingProvider.\nExt.direct.PollingProvider, provides for repetitive polling of the server at\ndistinct intervals (defaults to 3000 - every 3 seconds).\n\nAs we didn't set a function to our polling provider, if call it we should get an exception::\n\n >>> response = client.get('/polling/router/') #doctest: +NORMALIZE_WHITESPACE\n Traceback (most recent call last):\n ...\n RuntimeError: The server provider didn't register a function to run yet\n\nBut, as with ExtRemotingProvider, when Django it's in debug mode, the exception it's\nreturned to the browser::\n\n >>> settings.DEBUG = True\n >>> response = client.get('/polling/router/') \n >>> pprint(simplejson.loads(response.content)) #doctest: +NORMALIZE_WHITESPACE\n {u'message': u\"RuntimeError: The server provider didn't register a function to run yet\\n\",\n u'type': u'exception',\n u'where': [u'...',\n 311,\n u'router',\n u'raise RuntimeError(\"The server provider didn\\'t register a function to run yet\")']}\n\n >>> settings.DEBUG = False\n \nSo, let's declare a simple function an assign it to our polling provider::\n\n >>> from extdirect.django import polling\n >>> @polling(tests.polling_provider)\n ... def my_polling(request):\n ... return \"I'm tired...\"\n\n >>> response = client.get('/polling/router/') \n >>> pprint(simplejson.loads(response.content)) #doctest: +NORMALIZE_WHITESPACE\n {u'data': u\"I'm tired...\", u'name': u'some-event', u'type': u'event'}\n\n\nUsing the ExtDirectStore helper class\n-------------------------------------\n\nExtDirectStore it's a helper class that you may want to use to load a given\nExt.data.DirectStore in ExtJS.\n\nIt's important to note that you should use len=1 (python) and paramsAsHash=true (javascript) in\norder to get everything working\n\nLet's see the simplest use case::\n\n >>> from extdirect.django import ExtDirectStore\n >>> from extdirect.django.models import ExtDirectStoreModel\n >>> list = ExtDirectStore(ExtDirectStoreModel)\n >>> pprint(list.query()) #doctest: +NORMALIZE_WHITESPACE\n {'records': [{'id': 1, 'name': u'Homer'}, {'id': 2, 'name': u'Joe'}], 'total': 2}\n \nSo a quick and almost complete example could be:\n\nIn django::\n\n @remoting(provider, action='user', len=1)\n def load_users(request):\n data = request.extdirect_post_data[0]\n users = ExtDirectStore(User)\n return users.query(**data)\n\nIn ExtJS::\n\n new Ext.data.DirectStore({\n paramsAsHash: true, \n directFn: django.user.load_users, \n fields: [\n {name: 'first_name'}, \n {name: 'last_name'}, \n {name: 'id'}\n ],\n // defaults in django\n root: 'records',\n idProperty: 'id',\n totalProperty: 'total',\n ...\n }) \n \nAs we saw in the example above, you may want to pass a keyword arguments to the\nmethod `query` in order to filter your query::\n\n >>> pprint(list.query(id=1))\n {'records': [{'id': 1, 'name': u'Homer'}], 'total': 1}\n \nYou are able to change (or set at creation time) the keywords used by ExtDirectStore::\n\n >>> list.root = 'users'\n >>> list.total = 'result'\n >>> pprint(list.query())\n {'result': 2, 'users': [{'id': 1, 'name': u'Homer'}, {'id': 2, 'name': u'Joe'}]}\n \nIf you are using Paging, ExtDirectStore will take care::\n\n >>> pprint(list.query(start=0, limit=2))\n {'result': 2, 'users': [{'id': 1, 'name': u'Homer'}, {'id': 2, 'name': u'Joe'}]}\n\n >>> pprint(list.query(start=0, limit=1))\n {'result': 2, 'users': [{'id': 1, 'name': u'Homer'}]}\n\n >>> pprint(list.query(start=1, limit=1))\n {'result': 2, 'users': [{'id': 2, 'name': u'Joe'}]}\n \nAgain, you are free to change the keywords `start` and `limit` to whatever you want to::\n\n >>> list.start = 'from'\n >>> list.limit = 'to'\n >>> kw = {'from':0, 'to':1}\n >>> pprint(list.query(**kw))\n {'result': 2, 'users': [{'id': 1, 'name': u'Homer'}]}\n \nSorting it's also included::\n\n >>> pprint(list.query(sort='name', dir='ASC'))\n {'result': 2, 'users': [{'id': 1, 'name': u'Homer'}, {'id': 2, 'name': u'Joe'}]}\n\n >>> pprint(list.query(sort='name', dir='DESC'))\n {'result': 2, 'users': [{'id': 2, 'name': u'Joe'}, {'id': 1, 'name': u'Homer'}]}\n \nAnd guess what...? You are able to change this keywords too::\n\n >>> list.sort = 'sort_field'\n >>> list.dir = 'sort_order'\n \n >>> pprint(list.query(sort_field='name', sort_order='ASC'))\n {'result': 2, 'users': [{'id': 1, 'name': u'Homer'}, {'id': 2, 'name': u'Joe'}]}\n\n >>> pprint(list.query(sort_field='name', sort_order='DESC'))\n {'result': 2, 'users': [{'id': 2, 'name': u'Joe'}, {'id': 1, 'name': u'Homer'}]}\n \nFinally, sometimes you will need to run complex queries. We have two options for that.\nFirst, you could pass or set, an `extras` parameter to the ExtDirectStore. This should be a list of\ntuples like::\n\n >>> def name_size(rec):\n ... return len(rec.name)\n >>>\n >>> extras = [('name_size', name_size),('name_upper', lambda rec: rec.name.upper())]\n >>> list.extras = extras\n >>> pprint(list.query()) #doctest: +NORMALIZE_WHITESPACE\n {'result': 2,\n 'users': [{'id': 1,\n 'name': u'Homer',\n 'name_size': 5,\n 'name_upper': u'HOMER'},\n {'id': 2,\n 'name': u'Joe',\n 'name_size': 3,\n 'name_upper': u'JOE'}]}\n \n >>> list.extras = []\n\nEach item in the `extras` list should be a tuple with:\n - attribute name\n - callable object (taking only one required parameter)\n\nThe callable object in each tuple, will be executed for each object in the queryset\nto get the value for that attribute.\n\nThe second option to run complex queries it's very simple.::\n\n >>> qs = ExtDirectStoreModel.objects.exclude(id=2)\n >>> pprint(list.query(qs))\n {'result': 1, 'users': [{'id': 1, 'name': u'Homer'}]}\n \nHere, we just need to pass a valid queryset to the `query` function. Using this\nqueryset, ExtDirectStore, will apply everything that we already saw\n(filter, paging, sorting). You are able to create a complex queryset using\nall of the Django ORM features and then pass it to the method `query`.\n\nFinally, let's see what happen when you define ForeignKey in your models::\n\n >>> from extdirect.django.models import Model\n >>> ds = ExtDirectStore(Model)\n >>> pprint(ds.query())\n {'records': [{'fk_model': 1, 'fk_model_id': 1, 'id': 1}], 'total': 1}\n \nFor each, foreign key field (`fk_model`), you will get two attributes with the same value:\n - fk_model\n - fk_model_id\n\nCHANGES\n*******\n\n0.3 (2009-10-15)\n================\n\n* Bugfix: ExtDirectStore returns wrong 'total' parameter\n* Add a direct access to the descriptor API with an optional parameter\n `format=json` to get the descriptor as JavaScript code or as a JSON packet\n\n0.2 (2009-09-11)\n================\n\n* Faulty setup.py\n\n0.1 (2009-09-11)\n================\n\n* Initial public release",
"description_content_type": null,
"docs_url": null,
"download_url": "UNKNOWN",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "http://github.com/gsancho/extdirect.django/tree/master",
"keywords": "",
"license": "BSD",
"maintainer": null,
"maintainer_email": null,
"name": "extdirect.django",
"package_url": "https://pypi.org/project/extdirect.django/",
"platform": "UNKNOWN",
"project_url": "https://pypi.org/project/extdirect.django/",
"project_urls": {
"Download": "UNKNOWN",
"Homepage": "http://github.com/gsancho/extdirect.django/tree/master"
},
"release_url": "https://pypi.org/project/extdirect.django/0.3/",
"requires_dist": null,
"requires_python": null,
"summary": "Ext.Direct implementation for Django",
"version": "0.3"
},
"last_serial": 791818,
"releases": {
"0.1": [
{
"comment_text": "",
"digests": {
"md5": "90f8be10dab0e888de5993156ff6fde2",
"sha256": "c4c1dfae0c31a3c3ab6ec1f8f615ed2f15983244decc0003a346f812702754fe"
},
"downloads": -1,
"filename": "extdirect.django-0.1.tar.gz",
"has_sig": false,
"md5_digest": "90f8be10dab0e888de5993156ff6fde2",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 13551,
"upload_time": "2009-09-12T03:00:14",
"url": "https://files.pythonhosted.org/packages/fa/51/931d777d53587bb5739c632999285eb3813e7bc2382935e20ee9f498db7a/extdirect.django-0.1.tar.gz"
}
],
"0.2": [
{
"comment_text": "",
"digests": {
"md5": "7c2d2d9fc902c2b37deab64b2ebc8879",
"sha256": "306418b2f04af36815f4e8ac8e00429aa9d84b349a8042d8d82e817bfe751604"
},
"downloads": -1,
"filename": "extdirect.django-0.2.tar.gz",
"has_sig": false,
"md5_digest": "7c2d2d9fc902c2b37deab64b2ebc8879",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 12681,
"upload_time": "2009-09-12T04:06:12",
"url": "https://files.pythonhosted.org/packages/2a/39/4951ff23a2eded82a07dc92be61aea46ca923f86332a6982860e67d75be4/extdirect.django-0.2.tar.gz"
}
],
"0.3": [
{
"comment_text": "",
"digests": {
"md5": "bf0e9c1eac063bd2fb8db0c1b4059abc",
"sha256": "2883912bba37a618e1d3410101623ac05837f86b8170e3a0ededf8766e109044"
},
"downloads": -1,
"filename": "extdirect.django-0.3.tar.gz",
"has_sig": false,
"md5_digest": "bf0e9c1eac063bd2fb8db0c1b4059abc",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 15419,
"upload_time": "2009-10-15T16:58:21",
"url": "https://files.pythonhosted.org/packages/71/b7/35f46eb2cd6c164446e2f4e3577bcc18eb077aa9451dd713cfea9f9b6047/extdirect.django-0.3.tar.gz"
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "bf0e9c1eac063bd2fb8db0c1b4059abc",
"sha256": "2883912bba37a618e1d3410101623ac05837f86b8170e3a0ededf8766e109044"
},
"downloads": -1,
"filename": "extdirect.django-0.3.tar.gz",
"has_sig": false,
"md5_digest": "bf0e9c1eac063bd2fb8db0c1b4059abc",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 15419,
"upload_time": "2009-10-15T16:58:21",
"url": "https://files.pythonhosted.org/packages/71/b7/35f46eb2cd6c164446e2f4e3577bcc18eb077aa9451dd713cfea9f9b6047/extdirect.django-0.3.tar.gz"
}
]
}