{ "info": { "author": "Hari Mahadevan", "author_email": "hari@hari.xyz", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Environment :: Web Environment", "Framework :: Django", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python :: 2.7" ], "description": "Django CRUD through a single view\n=================================\n\nA single view implementation of table CRUD operations for Django. Single view \nmeans single URL to be registered in URL namespace. All CRUD operations are\ninvoked with the same URL but with URL arguments to distinguish them. This allows\nless crowded and a simpler URL namespace.\n\n## PROJECT NO LONGER MAINTANED ##\nI don't maintain or use this project anymore. I have since moved to a new CRUD that leverages Django's class based views. This is more modular and reuses all the good bits from core Django code. You can find it [here](https://github.com/harikvpy/django-popupcrud.git).\n\n# Introduction\n\nDjango comes with an excellent admin framework that provides a sophisticated \ninterface for table CRUD operations. However, the admin framework is closely \ntied to Django's default user management and its permission management systems.\nIf your project bypasses either of these, employing the CRUD in the admin \nframework can get a little tricky. \n\nSecondly, django admin also implicitly adds a number urls to your url \nnamespace. These urls list the apps whose models are are registered with it\nand for each app, the models in the app that have an admin CRUD interface. While\nthese can be forcefully removed by overriding the ModelAdmin class and using\nit to create your own admin based CRUD classes, managing and getting around\nits various dependencies can quickly get tedious to manage. And when Django gets\nupgraded, you have the job of reviewing the new admin interface to make sure\nthat it did not introduce any new 'holes' into your url namespace.\n\nThis project is aimed at addressing the above shortcomings by developing a pure \ndjango view that provides basic table CRUD operations. To use, derive from this \nview class providing it with the appropriate initialization parameters and then \nhook it up to the url namespace yourself explicitly.\n\n# Installation\n\n1. Easiest way to install crud is to get it from PyPi using pip. Do this by:: \n\n `pip install singleurlcrud`\n\n2. Add it to INSTALLED_APPS in projects ``settings.py``::\n```\nINSTALLED_APPS = (\n ...\n 'singleurlcrud',\n ...\n )\n```\n# Dependencies\n\n * django-bootstrap3\n * django-pure-pagination\n\n# Quickstart\n\nConsider the following model (taken from `polls` app, which is bundled with the \nsource code):\n```\nfrom django.db import models\n\nclass Question(models.Model):\n question_text = models.CharField(max_length=200)\n pub_date = models.DateTimeField('Date published')\n author = models.ForeignKey(Author, null=True, default=None)\n```\n\nTo get a fully functional CRUD for this table, declare a view like below:\n```\nfrom singleurlcrud.views import CRUDView\nfrom .models import Question\n\nQuestionCRUDView(CRUDView):\n model = Question\n list_display = ('question_text', 'pub_date', 'author')\n```\n\nThereafter, hook this view to the desired url through urls.py:\n```\nfrom django.conf.urls import url\nfrom .views import *\n\nurlpatterns = [\n url(r'^questions/$', QuestionCRUDView.as_view(), name='questions')\n ]\n```\n\nThat's it! You get a fully functional CRUD that will allow you to create,\nupdate and delete records from Question table, all rooted at \n`yoursite.com/questions/`.\n\n# Usage examples\nThis section documents the various common use case scenarios and how to \nimplement them using the CRUDView.\n\n## Custom multi-row actions\nTo enable action on a group of selected rows, override `get_actions()` method\nand return from it a list of 2-tuples where each tuple is of the form `(label,\nhandler,)`. Label is the label that will be displayed on the Actions drop down\nmenu and handler is the derived class method that will be invoked when user\nselects the corresponding action item.\n\nHandler method should be of the format\n```\ndef action_handler(self, request, qs):\n '''\n Parameters:\n request - the HttpRequest object\n qs - queryset containing the selected rows upon which the action\n is to be performed.\n\n Return:\n None - for view to refresh itself\n HttpResponse - to explicitly return a response object\n '''\n # do some action\n\n```\nNote that actions dropdown menu button (placed next to `Create New..` button) \nwill only be shown when there is at least one multi-row action item defined.\n\nTo illustrate with an example:\n```\nclass MyTableCRUDView(CRUDView):\n ...\n ...\n def get_actions(self):\n return [\n (_('Mark as done'), mark_as_done, ),\n ]\n \n def mark_as_done(self, request, qs):\n for obj in qs:\n obj.mark_as_done()\n return None # can be omitted for implicit return None\n```\n\n## Custom per-row actions\nPer row custom actions are supported through the callback `get_item_actions()`.\nThe return value from this method is a list of objects, one for each action,\nof the prototype ItemAction. When user selects the action, the corresponding\nItemAction object's doAction() method is invoked. This method is given the\nselected row as an argument.\n\nItemAction object has three class variables that need to initialized:\n\nVariable | Purpose \n-------- | ------- \n`title` | The `alt' text displayed for the action icon\n`key` | A unique string (amongst other actions) to identify this action\n`css` | CSS class for the icon element for this action\n\nThe following code example should make the options above clearer:\n```\nclass MyTableCRUD(CRUDView):\n\n class VoteAction(CRUDView.ItemAction):\n '''Per item custom action definition'''\n title = _('Mark as done')\n key = 'mark_as_done'\n css = 'glyphicon glyphicon-ok'\n\n def doAction(self, obj):\n obj.mark_as_done()\n\n```\n## Editing child models using formset\nSingleurlcrud supports editing child table rows using the Django FormSet\nmechanism. To facilitate this, override `get_formset_class()` method in your\nCRUDView derived class and return the `inline_formset` for the child model from\nthis method. \n\nFor our example project, `polls`, Author table's CRUD view is a good candidate\nto introduce inline formset editing as one author can create many questions\nand therefore the models are related by a foreign key. In order to achieve this \nwe just have to override `get_formset_class()` as below:\n\n```\nclass AuthorCRUDView(CRUDView):\n '''Author table CRUD'''\n model = Author\n list_display = ('name', 'email')\n\n def get_formset_class(self):\n return inlineformset_factory(\n Author, # parent model\n Question, # child model\n fields=['question_text', 'pub_date'], # fields for inline edit\n can_delete=True, # can rows be deleted?\n extra=1) # number of extra forms for adding new child entries\n```\nNote that here we're not using any custom forms and are leaving all the work to\nthe excelleng inlineformset_factory(). It builds a formset with individual\nforms for each child model instance and an extra form for entering new child\nmodel instance.\n\n# Reference\n\n## Options\nCRUDView provides many options which allows customizing its behavior. These are\ndocumented below:\n\n### `template_name`\nSpecifies the template that is used to render the list of items. This defaults to\n`singleurlcrud/list.html` and is rarely necessary to be customized.\n\n### `form_class`\nThe form class to be use for create and update operations. This is optional and\nif not spefified, CRUD will create a form using `modelform_factory` using the\nfields specified in `form_fields` option. If `form_fields` is not spefified,\nCRUDView will try to use the fields in `list_display`.\n\n### `allow_create`\nA boolean value, this controls whether the create operation is allowed. \nBy default it is allowed, that is, this is set to True. \n\n### `allow_edit`\nA boolean value, this controls whether the update operation is allowed. \nBy default it is allowed, that is, this is set to True. \n\n### `allow_delete`\nA boolean value, this controls whether the delete operation is allowed. \nBy default it is allowed, that is, this is set to True. \n\n### `context_object_name`\nThe context variable name that will be set to the object list for the list view.\nDefaults to `object_list`. You only need to customize this if you have a custom\ntemplate that want to use a different template variable name (for some reason).\n\n### `pagetitle`\nTitle of the list view page.\n\n### `table_css_classes`\nCSS classes applied to the table in list view. Defaults to \n`table table-striped table-condensed table-bordered`.\n\n### `list_display_labels`\nA dictionary that contains the labels to be used for each column in the list \nview. If not specified, column names will default to the field name specified\nin `list_display`. For callable column entries, attribute value\n`.short_description` is used as the column title.\n\n### `allow_multiple_item_delete`\nA boolean value, this controls whether multiple item deletion is allowed. \nMultiple item deletion is implemented using a checkbox against each item row\nand then selecting a dropdopwn menu item at the top. Set to `False` by default.\n\n### `related_field_crud_urls`\nA dictionary that has the CRUD url for each foreign key field of the model for\nwhich create and update operation through a popup window is to be enabled.\n\nNote that the view urls for the foreign key field models should also be \nimplemented using CRUDView for this to work.\n\n## Overridable methods\nLike options, CRUDView also provides many methods that can be overridden by the\nclient class to customize the CRUD behavior. Many of these methods are simple\nwrappers around class variables, provided to allow dynamic values to be \nreturned for the relevant options.\n\n### `get_form_class()`\nReturns the form class that will be instantiated for create and update \noperations. By default returns the value of `form_class` option, if it's \ndefined. If `form_class` is not defined, a `ModelForm` class for the model with\nfields set to either of the value of `form_fields` or `list_display` will be\nreturned.\n\n### `get_form(form_class, **kwargs)`\nReturns the form object to be used for create and update operations. \n`form_class` will be set to the return value of `get_form_class`. `**kwargs` \nwill contain additional arguments, such as form initial data for the update\noperation of CRUD, that are to be passed to the form constructor.\n\n### `get_form_fields()`\nReturn a tuple, that lists the fields of form used in create and update \noperations. Note that this method will only be called if a `form_class` is not\nspecified and `get_form()` is not overridden.\n\n### `get_formset_class()`\nCRUDView supports editing of child models using a formset. To activate this \nfeature, override this method and return the formset class to be used for inline\nediting of the child model instances. \n\nTypically one can use one of the django factory methods `inlineformset_factory`\nor `modelformset_factory()` to create this class.\n\nBy default this method returns `None` which disables child model editing.\n\n### `get_formset(formset_class, **kwargs)`\nReturn the formset class instance to be used for editing child model instances.\n\n### `get_related_field_crud_urls()`\nWrapper around the class option `related_field_crud_urls`. By default returns\nthe value assigned to option variable `related_field_curd_urls`.\n\n### `get_add_item_custom_url()`\nReturn a custom url, presumably with its own view that you write, that you want \nto use for the create operation. By default returns `None`.\n\n### `get_edit_item_custom_url()`\nReturn a custom url, presumably with its own view that you write, that you want \nto use for the update operation. By default returns `None`.\n\n### `get_delete_item_custom_url()`\nReturn a custom url, presumably with its own view that you write, that you want \nto use for the delete operation. By default returns `None`.\n\n### `get_item_template(self)`\nReturns the template used to render each item in list view. Template returned \nby this method is used to render each row of the model in list view. You can \noverride this to customize per item rendering.\n\nFor example, by default each row of the table is given on table row. But for \nyour model, you might want to render additional rows listing the child model\ninstances associated with the model row. You can acheive this by overriding this\nmethod to return a custom template.\n\n### `get_pagetitle()`\nWrapper for `pagetitle` class options variable.\n\n### `get_allow_create()`\nWrapper for `allow_create` class option. Method allows for determining this\nvalue during runtime rather than static definition in the code.\n\n### `get_allow_edit()`\nWrapper for `allow_edit` class option. Method allows for determining this\nvalue during runtime rather than static definition in the code.\n\n### `get_allow_delete()`\nWrapper for `allow_delete` class option. Method allows for determining this\nvalue during runtime rather than static definition in the code.\n\n### `get_allow_multiple_item_delete()`\nWrapper for `allow_multiple_item_delete` class option. Method allows for determining this\nvalue during runtime rather than static definition in the code.\n\n### `get_disallowed_create_message()`\nOften times you might want to control the number of rows that a user can\ncreate on a table. Or you might want to limit row creation based on user roles.\nWhen such logic is determined dyanamically and the creation operation is \ndisallowed, you can display an alert message when the table CRUD is activated.\n\nThis method allows you to specify the custom message that will be displayed on\ntop of the list view (where the Create New.. button would've been) \ninforming the user that row creation is disallowed.\n\n### `get_breadcrumbs()`\nIf your site supports breadcrumbs, override this method to return a list of\nbreadcrumbs that depicts the navigation path to the CRUD url. Each item of\nthis list is a 2-tuple of the form `(text, url)` where `text` is to be added to\nthe breadcrumbs hyperlinking it to `url`.\n\nBreadcrumbs returned from this method are passed to the context through the \ncontext variable `breadcrumbs`. Ideally, your project's base template should \nhandle this list by rendering each item in the list as an appropriately\nstyled `
  • ` or something similar.\n\n### `get_actions()`\nReturn a list of tuples where each tuple consists of `(label, handler,)` where\n`label` will be displayed in the action dropdown and `handler` is a method\nin the derived class that is to be invoked when the user selects the action.\n\n### `get_item_actions()`\nReturn a list of ItemAction derived objects that represent the additional item \nspecific action to be invoked for each item in the itemlist. When the action is\nselected, the corresponding ItemAction object's `doAction()` method will be \ninvoked. ItemAction has the following prototype:\n\n```\nclass ItemAction(object):\n title = ''\n key = ''\n css = ''\n\n def doAction(self, item):\n pass\n```\n\n### `item_deletable(object)`\nReturn a boolean to indicate if the object can be deleted. By default, True is \nreturned by the base class. If False is returned for any object, the delete \noption for that item will be disabled.\n\nThis method, alongwith `item_editable` below, allows controlling per item delete\nand edit operations based on the row or some other dynamic property.\n\n### `item_editable(object)`\nSame as `item_deletable` above, but works for updating an item.\n\n## Helper methods\n### `return_as_href(label, urlname, kwargs)`\nThis helper method return a well formed anchor element composed of its three\narguments of the form:\n```\n label\n```\nThis helper can be used to conveniently return an anchor element from a method\nthat is listed as one of the columns in list view.\n\n\nLicense\n-------\n\nModified BSD\n\nAuthor\n------\n\n`Hari Mahadevan `_\n\n\nHistory\n-------\n\n0.21 - 2017/10/16\n++++++++++++++++\n- Update for project status. Add link to ``django-popupcrud``.\n\n0.20 - 2016/1/30\n++++++++++++++++\n- Add support for specifying list column titles through a dictionary specified\n as the class variable - list_display_lables\n\n0.19 - 2016/1/24\n++++++++++++++++\n- Fix mispelt context variable item_actions (was referred to as itemactions)\n\n0.18 - 2016/1/22\n++++++++++++++++\n- Fix formatting errors in README.rst.\n\n0.17\n++++\n- Move changelog to HISTORY.rst and include it in setup long_description\n through embedded script.\n\n0.16\n++++\n- Fix more errors in setup.py that stopped pip install from working\n\n0.15\n++++\n- Fix errors in setup.py that stopped pip install from working\n\n0.14\n++++\n- Fix errors in setup.py.\n- Update status to '4 - Beta'.\n \n0.13\n++++\n- Use django-pure-pagination for pagination. This provides margin page\n numbers which provides a nice UX for listing tables with very large\n amounts of data, number of pages for which exceed the available \n width in the screen.\n\n0.12\n++++\n- Move delete operation into an independent GET action through the \n '?o=delete' parameter.\n\n0.11\n++++\n- Action buttons changed to use buttons grouping them into a btn-group.\n- Added colors to the buttons indicating the severity of the action's outcome.\n\n0.10\n++++\n- Changed table css classes to be specified as view setting provided\n through RequestContext.\n \n0.9\n+++\n- Refactor cryptic flag names to more friendly names. Eg.: can_delete() has\n been changed to item_deletable(). \n- Global flags can_create, can_edit and can_delete has been replaced by \n enable_create, enable_edit & enable_delete respectively.\n\n0.8\n+++\n- Add support for view to customize page titles by specifying a class\n variable 'pagetitle'. This title will be used by default and if not\n specified the model's verbose_name_plural will be set as the title\n in the context.\n\n0.7\n+++\n- Fix media property such that it only returns media fragments necessary\n for the current CRUD operation.\n\n0.6\n+++\n- Fix incorrect arguments to can_delete() method call.\n\n0.5\n+++\n- When the derived class specifies a custom form by overriding the\n get_form() method, inline editing/addition of RelatedField objects\n is not available. This version includes a fix for this.\n\n0.4\n+++\n- Support for per item editing control through the item property\n item.can_edit, which should return a boolean indicating if editing\n is allowed. Defaults to True, if the property is missing.\n\n0.3\n+++\n- Support for per item deletion control through the item property\n item.can_delete, which should return a boolean indicating if deletion\n is allowed. Defaults to True, if the property is missing.\n\n0.2\n+++\n- Support for inline editing/addition of RelatedField objects through\n a popup window. Note that base template has to be designed\n to accommodate this feature by removing the embellishments that adorn a \n regular page.\n\n0.1\n+++\n- Initial release", "description_content_type": null, "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://www.github.com/harikvpy/crud/", "keywords": "django,crud,singleurlcrud", "license": "BSD-3", "maintainer": "", "maintainer_email": "", "name": "singleurlcrud", "package_url": "https://pypi.org/project/singleurlcrud/", "platform": "", "project_url": "https://pypi.org/project/singleurlcrud/", "project_urls": { "Homepage": "https://www.github.com/harikvpy/crud/" }, "release_url": "https://pypi.org/project/singleurlcrud/0.21/", "requires_dist": null, "requires_python": "", "summary": "Django CRUD using a single view and hence a single URL.", "version": "0.21" }, "last_serial": 3252581, "releases": { "0.13": [ { "comment_text": "", "digests": { "md5": "2efb30607d5d9aeb7b1f5e324223eeeb", "sha256": "f2fb7ab775478a4d03c4eb75629089ff120d23ae28ceb1436b112e13bfdaa92d" }, "downloads": -1, "filename": "singleurlcrud-0.13.tar.gz", "has_sig": false, "md5_digest": "2efb30607d5d9aeb7b1f5e324223eeeb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 24446, "upload_time": "2016-01-21T09:33:31", "url": "https://files.pythonhosted.org/packages/19/76/07b30e7e92b445b119dba8bb8266e121140fafd5a0b22d89e5ccd9da631d/singleurlcrud-0.13.tar.gz" } ], "0.14": [ { "comment_text": "", "digests": { "md5": "45aec42fbbdde1bea7870ac9d9cc0909", "sha256": "a72b5488b3e541f2b594923949305bc70fd7ffcfdcf0c64d66381664ff5cd076" }, "downloads": -1, "filename": "singleurlcrud-0.14.tar.gz", "has_sig": false, "md5_digest": "45aec42fbbdde1bea7870ac9d9cc0909", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 24528, "upload_time": "2016-01-21T14:18:39", "url": "https://files.pythonhosted.org/packages/02/95/a1a38e14d9e4db2c795bc9f124e5c33f1d98fc375a933bfe66c324067101/singleurlcrud-0.14.tar.gz" } ], "0.16": [ { "comment_text": "", "digests": { "md5": "80b5367ec9e65221d01e361411fe649e", "sha256": "9ae2e3d92ef7080556edece99edd4ea175ecf32e97b31bf05fc369236e4b236c" }, "downloads": -1, "filename": "singleurlcrud-0.16.tar.gz", "has_sig": false, "md5_digest": "80b5367ec9e65221d01e361411fe649e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 25105, "upload_time": "2016-01-21T16:06:53", "url": "https://files.pythonhosted.org/packages/36/08/a6dd2be38a9818fc7809d8af6840e8c33c16b78359dd7890f36751f9a09b/singleurlcrud-0.16.tar.gz" } ], "0.17": [ { "comment_text": "", "digests": { "md5": "acbe51dfd7f53d7acfa95a0ffd83fc1d", "sha256": "b74a2123015b0550c1c39d2e89e1f3b727da9070bac090b01a253455cf6a48ed" }, "downloads": -1, "filename": "singleurlcrud-0.17.tar.gz", "has_sig": false, "md5_digest": "acbe51dfd7f53d7acfa95a0ffd83fc1d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 25466, "upload_time": "2016-01-21T16:43:19", "url": "https://files.pythonhosted.org/packages/85/4d/3711909da95d75d00875dcdb2431806d3f3997ff6f77e847a66a73b90611/singleurlcrud-0.17.tar.gz" } ], "0.18": [ { "comment_text": "", "digests": { "md5": "89c07116485547b097baeac57418e68b", "sha256": "d42ace6e1ef479eb6b0f472a5970c98b49a86bc6940a7b5ab587fcbb7ceb6cc8" }, "downloads": -1, "filename": "singleurlcrud-0.18.tar.gz", "has_sig": false, "md5_digest": "89c07116485547b097baeac57418e68b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 26318, "upload_time": "2016-01-21T16:49:18", "url": "https://files.pythonhosted.org/packages/2d/31/e84d2aa6fbd18b26e39855a47fb98cdb02389a94b944a41e4004ff5c798b/singleurlcrud-0.18.tar.gz" } ], "0.19": [ { "comment_text": "", "digests": { "md5": "fb4110ab120d3cc0af082dce00252d35", "sha256": "78241a4a6e3a09ba109e12be79d33a6198d93a7ac4cc5233099f06a44ca8e8e0" }, "downloads": -1, "filename": "singleurlcrud-0.19.tar.gz", "has_sig": false, "md5_digest": "fb4110ab120d3cc0af082dce00252d35", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 26435, "upload_time": "2016-01-24T08:54:03", "url": "https://files.pythonhosted.org/packages/fb/fa/ca8ce5d97a7a0d3831d2ce92c0c32b826b92cfd49cecf36a65bdc84eb91f/singleurlcrud-0.19.tar.gz" } ], "0.21": [ { "comment_text": "", "digests": { "md5": "34276b91b93bda1b118a652fee603807", "sha256": "2e131573e8139732a9f555c2742dceeee6604c1dc2d3eb02b2ec7f6830450b03" }, "downloads": -1, "filename": "singleurlcrud-0.21.tar.gz", "has_sig": false, "md5_digest": "34276b91b93bda1b118a652fee603807", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30296, "upload_time": "2017-10-16T03:10:21", "url": "https://files.pythonhosted.org/packages/07/b0/f5f2ea6d5b169cce4011a91445f5a841dac99e7503e86630cf25b2180168/singleurlcrud-0.21.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "34276b91b93bda1b118a652fee603807", "sha256": "2e131573e8139732a9f555c2742dceeee6604c1dc2d3eb02b2ec7f6830450b03" }, "downloads": -1, "filename": "singleurlcrud-0.21.tar.gz", "has_sig": false, "md5_digest": "34276b91b93bda1b118a652fee603807", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30296, "upload_time": "2017-10-16T03:10:21", "url": "https://files.pythonhosted.org/packages/07/b0/f5f2ea6d5b169cce4011a91445f5a841dac99e7503e86630cf25b2180168/singleurlcrud-0.21.tar.gz" } ] }