{ "info": { "author": "Paul Rutledge", "author_email": "paul.v.rutledge@gmail.com", "bugtrack_url": null, "classifiers": [ "Environment :: Web Environment", "Framework :: Django", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: Dynamic Content" ], "description": "[![PyPI version](https://badge.fury.io/py/django-commands.svg)](https://badge.fury.io/py/django-commands)\n\n## What\nDjango-commands is a reusable django app that helps idiot-proof and\nsimplify the process of writing client-side and server-side\ncode for communicating via ajax. A command handler is a replacement \nfor the standard django view and is intended to deal strictly with ajax \nrequests. Requests are actually sent as form data so that files and params\ncan be sent together. Params will be json encoded, files will be left as is.\n\n## How\nBy using a plugin-architecture and auto-discovery of modules across\nyour existing apps, django-commands removes the need to have the same \nsort of redundant boilerplate validation for views intended to handle ajax requests.\nUltimately, it allows operations to be more driven by the business\nlogic of the application and less bound to models like what is often\nseen today in CRUD scaffolding demos and the like. Note that it is NOT restful. Instead\nit follows a pattern known as RPC (remote procedure call).\n\n## Why\nAjax gets messy and resource-bound RESTful API routes are not always\nthe best solution for applications with more complex business logic. Rather\nthan struggling through the boilerplate view definition and basic validation\nof a request, django-commands allows you to focus solely on your business logic\nbecause you can be sure that anything that reaches your #handle method was\nvalid in terms of parameter existence, parameter type, user authentication,\nand user permissions. It's only up to you to decide if it's valid based on your \nbusiness rules.\n\n## Goals\n- Keep the implementation of a command handler simple, explicit, and elegant.\n- Should be no reason to have to venture outside of the command handler strategy for any\n form posts or ajax procedures within an application.\n- Perform thorough validation of a request on front-end and back-end so that\n developers get immediate and telling feedback.\n- Developers should not have to write boilerplate JavaScript to get access to their\n commands. We have enough information to generate a simple JS client.\n- Allow commands to take arbitrary amount of keys consisting of data of \n the following types and correctly upload them, pass thorough validation, \n and reach the handler in a directly usable format.\n - Blob\n - File\n - String\n - Float\n - Integer\n - Boolean\n - Object*\n - String Array\n - Float Array\n - Integer Array\n - Boolean Array\n - Object* Array\n\n_*For django-commands, object types consist of a standard JavaScript object that\nmay combine any of the other types except for Blobs and Files. Currently there\nis no specification for expressing nested types requirements, so specifying the\nobject type only guarantees you'll receive a dictionary in the command handler._\n\n## Be Aware\n- 'user' is a reserved parameter name. The user for a given request will be available\nunder self.user inside of a command handler #handle method. Validators for 'user' can\nbe implemented and will be called appropriately.\n\n## Installation\n\n### Get the package\n```bash\npip install django-commands\n```\n\n### Register as an installed application\n```python\n#settings.py\nINSTALLED_APPS = (\n\t'commands',\n)\n```\n\n## Usage\n\n### Setup Routes\nAdd this one line to your root urls.py. You can change the actual route if you want and\nthe correct endpoints will still be set inside the client-side code, just don't change the \nnamespace. \n```python\nurl(r'^commands/', include('commands.urls', namespace='commands')),\n```\n\n### Create Command Handler\nInside your custom apps where you want to use commands, create a file `commands.py`\n\nInside of that file you define your logic for all the commands you want available for that application. I've given\na simple example below.\n\n```python\nfrom commands.base import *\nfrom commands.types import *\nfrom commands.decorators import *\nfrom .models import *\n\nclass MyCommandHandler(CommandHandlerBase):\n\n # the name of the command\n command_name = 'SOME_CANONICAL_COMMAND_NAME'\n\n # parameters that are required in order to be considered a valid command request\n params = [\n Param('number', Types.INTEGER),\n Param('message', Types.STRING),\n Param('counts', Types.INTEGER_ARRAY)\n ]\n\n # request won't even make it to the handle method if they aren't authenticated\n auth_required = True\n \n # request won't even make it to the handle method if they don't have the permissions listed.\n permissions = ['superuser']\n \n # if all validation based on the static fields passes, then this class is instantiated\n # and the request and the appropriate data is passed into the command_data\n def handle(self, data):\n \n instance = MyModel.objects.get(number = data.number)\n instance.message = data.message\n instance.sum = sum(data.counts)\n instance.save()\n return self.success({'responseMessage': 'Woohoo! You win!'})\n \n \n # you can implement @validator decorated methods for some additional validation\n # prior to instantiation / reaching the #handle method. Here we check that number\n # is not negative\n \n @validator('number', 'The number parameter cannot be negative.')\n def validate_number(number):\n return number >= 0\n```\n\n### Include Static Files\nJust add the following line to the header of whichever\npages you plan to be using ajax commands. The front-end scripts\nassume that you are already using jQuery.\n```jinja2\n{% include 'commands/scripts.html' %}\n```\n\n### Use The Commands\n```JavaScript\n\n// this will make the front-end aware of all the available\n// commands that you've defined in commands.py files across\n// all of your apps. All available commands (based on user permissions\n// and authentication status will be populated into the global commands object.\n// note, this only needs to be done if you don't predefine the available commands (see below with AMD)\ncommands.UpdateDefinition();\n\n// this will attempt to post the command to your handler,\n// but first it will do some checks to make sure you have\n// the required data parameters and that they are of the\n// right type. This validation occurs on the front end\n// and on the backend. This call without data won't work\n// for the command that we defined.\ncommands.SOME_CANONICAL_COMMAND_NAME.fire();\n\n// defining legitimate data\nvar data = {number: 5, message: 'my message', counts: [5, 6, 7]};\n\n// if status==200 on the response (they called return self.success())\nvar successHandler = function(data){\n\talert(data.result.responseMessage); // alerts Woohoo! You Win!\n}\n\n// if status!=200 on the response (they called return self.error() or validation failed)\nvar errorHandler = function(data){\n\talert(data.error);\n}\n\n// making the actual call with data and callbacks\ncommands.SOME_CANONICAL_COMMAND_NAME.fire(data, successHandler, errorHandler);\n\n\n// What if number was negative?\nerrorHandler = function(data){\n alert(data.errors['number']);\n}\n\ndata.number = -1;\n\n// This will alert: 'The number parameter cannot be negative.'\ncommands.SOME_CANONICAL_COMMAND_NAME.fire(data, successHandler, errorHandler);\n```\n\n\n### AMD\nDjango commands also supports loading via AMD by following the universal module definition pattern. Note that you also preload the available commands on the page by using the ```{% commands %}``` template tag and setting it equal to a variable.\n\nIn your base template:\n```jinja2\n{% load commands %}\n\n...\n\n\n```\n\n\nThen, in your requirejs configuration:\n```javascript\nrequire.config({\n\n\t\tpaths: {\n\t\t\t'commands': 'commands/commands'\n\t\t},\n \t//...\n\t\tconfig: {\n\t\t\tcommands: {\n\t\t\t\tavailableUrl: '/commands/available/',\n\t\t\t\texecutionUrl: '/commands/',\n\t\t\t\tcommands: MY_GLOBAL_VARIABLE.commands\n\t\t\t}\n\t\t},\n \t//...\n});\n```", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/RutledgePaulV/django-commands", "keywords": "django command strategy plugin", "license": "MIT", "maintainer": null, "maintainer_email": null, "name": "django-commands", "package_url": "https://pypi.org/project/django-commands/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/django-commands/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/RutledgePaulV/django-commands" }, "release_url": "https://pypi.org/project/django-commands/0.8/", "requires_dist": null, "requires_python": null, "summary": "A django app that provides a plugin type model for integrating the front and backend.", "version": "0.8" }, "last_serial": 1849586, "releases": { "0.5": [ { "comment_text": "", "digests": { "md5": "448aeeb4e0284b4edb0b7e58c3ca6941", "sha256": "a759cf0b246171e5297fcf476f2a710af92640a12b95d231d560525053fb6a2c" }, "downloads": -1, "filename": "django-commands-0.5.tar.gz", "has_sig": false, "md5_digest": "448aeeb4e0284b4edb0b7e58c3ca6941", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16143, "upload_time": "2014-11-03T00:58:36", "url": "https://files.pythonhosted.org/packages/7c/da/bc5b007a0d67ed4a731f7ec975014efa12260e4812d981910c206068321b/django-commands-0.5.tar.gz" } ], "0.6": [ { "comment_text": "", "digests": { "md5": "8d20d19a439bfa7f1ec54deb9d914ada", "sha256": "2ea447e55a60e1f3af26a6897976d1e08e50550c487523ab3849bcbfa9f079e6" }, "downloads": -1, "filename": "django-commands-0.6.tar.gz", "has_sig": false, "md5_digest": "8d20d19a439bfa7f1ec54deb9d914ada", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16736, "upload_time": "2014-11-08T23:23:02", "url": "https://files.pythonhosted.org/packages/6a/ba/cb66c22f778d905b49b72d0b5519238cea095e9e56b41cc9464058b23f8e/django-commands-0.6.tar.gz" } ], "0.7": [ { "comment_text": "", "digests": { "md5": "5e8ebae379008173a9b6395299e6ce70", "sha256": "561caaf2caec24bc85afddc4ba7694676883dd08b52a46e60be2edd854f5d829" }, "downloads": -1, "filename": "django-commands-0.7.tar.gz", "has_sig": false, "md5_digest": "5e8ebae379008173a9b6395299e6ce70", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14876, "upload_time": "2015-05-11T22:58:27", "url": "https://files.pythonhosted.org/packages/ba/93/e862c07e7fb694055e82985feb40351caa07fd565aa25072b3f57c05ab31/django-commands-0.7.tar.gz" } ], "0.8": [] }, "urls": [] }