{ "info": { "author": "(the author)", "author_email": "mdilligaf@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "Django Display Exceptions\n---------------------------------------\n\nThis app can (slightly) encourage modularity and readability, as well as decrease code repetition and length, by using Exceptions to handle exceptional (non-standard) situations.\n\nSpecifically, it offers an alternative way to handle a failing check in a view fails (object not found, access denied, etc). It let's you throw an special exception that is shown to the user in a pretty way. No need to worry about returning error messages up the chain.\n\nThe 'problem' (or 'inconvenience' really) that this solves is explained in my programmers.stackexchange_ question, which is where the idea for started.\n\nExample usage\n---------------------------------------\n\nThere is a simple example (the whole app is simple, really) contained in the de_demo directory of this repository. For a preview, check the last code block in this subsection.\n\nLet's say you have an app and you want to be able to edit some object belonging to a user, like their account. Normally you would do something like::\n\n\tdef user_update_name(request):\n\t\t# first find the user we're trying to edit\n\t\ttry:\n\t\t\tediting_user = get_user_model().objects.get(**{\n\t\t\t\tget_user_model().USERNAME_FIELD: request.GET['user']\n\t\t\t})\n\t\texcept (IndexError, MultiValueDictKeyError):\n\t\t\treturn render(request, 'not_found.html', {\n\t\t\t\t'message': 'A username is needed to look up a user.',\n\t\t\t\t'next': reverse('home'),\n\t\t\t})\n\t\texcept get_user_model().DoesNotExist:\n\t\t\treturn render(request, 'not_found.html', {\n\t\t\t\t'message': 'No user by the name \"{0:s}\".'.format(request.GET['user']),\n\t\t\t\t'next': reverse('home'),\n\t\t\t})\n\t\t# now check that we have permission to edit users\n\t\tif not request.user.is_authenticated():\n\t\t\treturn render(request, 'permission_denied.html', {\n\t\t\t\t'message': 'You need to login to be able to do this (\"{0:s}\").'.format('change_user'),\n\t\t\t\t'next': reverse('login'),\n\t\t\t})\n\t\tif not request.user.has_perm('change_user'):\n\t\t\treturn render(request, 'permission_denied.html', {\n\t\t\t\t'message': 'You do not have permission to this operation (\"{0:s}\").'.format('change_user'),\n\t\t\t\t'next': reverse('home'),\n\t\t\t})\n\t\t# finally check that we're editing our own account, or that we're a staff member\n\t\tif not request.user.pk == editing_user.pk or request.user.is_staff:\n\t\t\treturn render(request, 'permission_denied.html', {\n\t\t\t\t'message': 'You can only edit your own account. You can continue to your own account, or use your browser\\'s back button',\n\t\t\t\t'header': 'Don\\'t mess with other people!',\n\t\t\t\t'next': '{0:s}?user={1:s}'.format(reverse('user_update_email'), request.user.username),\n\t\t\t})\n\t\t# handle the name update here\n\nLooks secure! But now we want another view to update the name. Also we'll want to view users, which only requires the first set of checks. So we make some functions, to not repeat ourselves::\n\n\t# one of the functions (others are implied)\n\tdef try_to_get_user(dic, key = 'user'):\n\t\ttry:\n\t\t\tuser = get_user_model().objects.get(**{\n\t\t\t\tget_user_model().USERNAME_FIELD: dic[key]\n\t\t\t})\n\t\texcept (IndexError, MultiValueDictKeyError):\n\t\t\treturn render(request, 'not_found.html', {\n\t\t\t\t'message': 'A username is needed to look up a user.',\n\t\t\t\t'next': reverse('home'),\n\t\t\t})\n\t\texcept get_user_model().DoesNotExist:\n\t\t\treturn render(request, 'not_found.html', {\n\t\t\t\t'message': 'No user by the name \"{0:s}\".'.format(request.GET['user']),\n\t\t\t\t'next': reverse('home'),\n\t\t\t})\n\t\treturn user\n\n\t# the updated view; note that user_update_email looks almost identical\n\tdef user_update_name(request):\n\t\t# first find the user we're trying to edit\n\t\tuser_or_error = try_to_get_user(request.GET)\n\t\tif not isinstance(user_or_error, get_user_model()):\n\t\t\treturn user_or_error\n\t\telse:\n\t\t\tuser = user_or_error\n\t\t# now check that we have permission to edit users & that we're editing our own account, or that we're a staff member\n\t\tpossible_error_one = check_user_access(request.user, 'change_user')\n\t\tpossible_error_two = check_self_only(request.user, user)\n\t\tif possible_error_one or possible_error_two:\n\t\t\treturn possible_error_one or possible_error_two\n\t\t# handle the email update here\n\nThat is a bit better, but there's still a lot of checking going on in our views. And the dynamic typing abuse isn't exactly beautiful.\n\nSo maybe we can handle the exceptional situations using Exceptions? If we use normal ones, we have to catch them in the main view, which will have us repeat a lot of code again. So this is where Django Display Exceptions comes in! We simply raise displayable exceptions::\n\n\t# one of the functions (others are implied)\n\tdef try_to_get_user(dic, key = 'user'):\n\t\ttry:\n\t\t\tuser = get_user_model().objects.get(**{\n\t\t\t\tget_user_model().USERNAME_FIELD: dic[key]\n\t\t\t})\n\t\texcept (IndexError, MultiValueDictKeyError):\n\t\t\traise NotFound('A username is needed to look up a user.', next = reverse('home'))\n\t\texcept get_user_model().DoesNotExist:\n\t\t\traise NotFound('No user by the name \"{0:s}\".'.format(dic[key]), next = reverse('home'))\n\t\treturn user\n\n\t# the twice updated view; note that user_update_email looks almost identical\n\tdef user_update_email(request):\n\t\tuser = try_to_get_user(request.GET)\n\t\tcheck_user_access(request.user, 'change_user')\n\t\tcheck_self_only(request.user, user)\n\t\t# handle the email update here\n\n*Shorter, more readable, almost no code repetition and no dynamic typing abuse*!\n\nNote that these are special exceptions. If some other error occurs, it will be handled just like it normally would; it will not be rendered by Django Display Exceptions.\n\nConfiguration\n---------------------------------------\n\nI know what you're thinking: *by the gods, this is genius, I want in on this!*\n\nSetup is easy: install with pip in your virtual environment (or globally, I won't judge)::\n\n\tpip install django_display_exceptions\n\nSecond, add ``display_exceptions`` to ``INSTALLED_APPS``::\n\n\tINSTALLED_APPS = (\n\t\t'display_exceptions',\n\t\t...\n\t)\n\nIf you want to override the exception templates, you will have to place the override app below ``display_exceptions``. That's the only condition, so might as well place ``display_exceptions`` somewhere at the top.\n\nThird, add the middleware that will handle displaying the exceptions::\n\n\tMIDDLEWARE_CLASSES = (\n\t\t...\n\t\t'display_exceptions.DisplayExceptionMiddleware',\n\t)\n\nIn this case, you probably want Django Display Exceptions to do it's thing before before any logging or fallbacks or anything. This means that it should be below any such middleware (since it's an exception, which are handled in the same order as responses). So put it somewhere at the bottom.\n\nThere are no migrations. In production, if you want to use the default templates, you'll have to call ``collectstatic``.\n\nThat is all; you're good to go!\n\nBuilt-in displayable exceptions\n---------------------------------------\n\nThe exceptions that are built in, and that are caught by the middleware:\n\n* *PermissionDenied* (550 Permission Denied): the current account doesn't have access to this resource.\n* *NotFound* (404 Not Found): whatever the user requested could not be found (temporarily or permanently).\n* *BadRequest* (400 BadRequest): what the user sent is not correctly formatted (e.g. non-integer id).\n* *NotYetImplemented* (501 Not Implemented): the requested functionality isn't supported yet.\n* *Notification* (200 Ok): no error, just display something.\n\nIf there's no suitable exception in the list, you can subclass ``DisplayableException`` yourself.\n\nArguments\n---------------------------------------\n\nThe exceptions take several arguments that influence their rendering:\n\n* *message*: The message to be displayed, describing what went wrong.\n* *caption*: If set, overrules the default caption for the error display page.\n* *next*: The url of the page the user should continue, or a callable to generate said url.\n* *status_code*: If set, overrules the default http status code of this exception.\n* *template*: If set, overrules the default template used to render this exception.\n* *context*: Any extra context for the template (only useful for custom templates).\n\nCheck out the docstring for ``DisplayableException`` for all the arguments.\n\nCustomization\n---------------------------------------\n\nThe above arguments are useful for per-exception customization, but perhaps you want to integrate the overall look into your site. There are several options.\n\nYou can change the base template used for exceptions in settings::\n\n\tDISPLAY_EXCEPTIONS_BASE_TEMPLATE = 'exceptions/base.html'\n\nUnless you're also overriding all the derived templates, your base template should contain the blocks ``caption``, ``message``, `icon`` and ```actions`` (for buttons).\n\nYou can also override templates for each of the exceptions. Just create a file called for example ``exceptions/permission_denied.html`` (see notes for ``INSTALLED_APPS`` order). If you want to use the exception base template, these templates should::\n\n\t{% extends EXCEPTION_BASE_TEMPLATE %}\n\nand implement the blocks mentioned.\n\nFinally, you can change the rendering function, using ``settings.DISPLAY_EXCEPTIONS_RENDER_FUNC``. It should accept ``request``, ``exception`` and any ``**kwargs`` (forward compatibility).\n\nHandling standard problems\n---------------------------------------\n\nThis app is not intended for handling real, unexpected problems, which is why internal server errors aren't included.\n\nThat said, if you want some placeholder error handlers anyway, you can put this in your root `urls.py`::\n\n\thandler400 = raise_bad_request_exception\n\thandler403 = raise_permission_denied_exception\n\thandler404 = raise_not_found_exception\n\nRemember that these only appear if `DEBUG = False`.\n\nLicense\n---------------------------------------\n\nRevised BSD License; at your own risk, you can mostly do whatever you want with this code, just don't use my name for promotion and do keep the license file.\n\n.. _programmers.stackexchange: http://programmers.stackexchange.com/questions/276302/how-to-handle-django-get-single-instance-in-view-pattern", "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/mverleg/django_display_exception", "keywords": "django,coding-style", "license": "Revised BSD License (LICENSE.txt)", "maintainer": null, "maintainer_email": null, "name": "django-display-exceptions", "package_url": "https://pypi.org/project/django-display-exceptions/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/django-display-exceptions/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/mverleg/django_display_exception" }, "release_url": "https://pypi.org/project/django-display-exceptions/1.8/", "requires_dist": null, "requires_python": null, "summary": "This app can (slightly) encourage modularity and readability, as well as decrease code repetition and length, by using Exceptions to handle exceptional (non-standard) situations.", "version": "1.8" }, "last_serial": 2555813, "releases": { "1.0": [ { "comment_text": "", "digests": { "md5": "7fc2a7689bb68a8d16a37dd6c44b54c4", "sha256": "4607df66a7a1c3d0322f161c97aab1e0827d0c42ec3ab6897a52a225900e0631" }, "downloads": -1, "filename": "django-display-exceptions-1.0.tar.gz", "has_sig": false, "md5_digest": "7fc2a7689bb68a8d16a37dd6c44b54c4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 144366, "upload_time": "2015-10-14T19:32:22", "url": "https://files.pythonhosted.org/packages/5a/30/aa7e10e416505f6fa7ab9d60f07a800a2a7257dfc39c8c43df870c5013c5/django-display-exceptions-1.0.tar.gz" } ], "1.2": [ { "comment_text": "", "digests": { "md5": "9080f40b8cfeab10c82c817b8d285677", "sha256": "e1cab083154e7ab75a3a67ee1436074c09726f8f86a071d0b816fd7f2f860aac" }, "downloads": -1, "filename": "django-display-exceptions-1.2.tar.gz", "has_sig": false, "md5_digest": "9080f40b8cfeab10c82c817b8d285677", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 144327, "upload_time": "2015-10-15T01:53:12", "url": "https://files.pythonhosted.org/packages/e5/96/57dbdfd11c3912d3177994500d746ef330c33eaefdea5d7fad67fcc92aa3/django-display-exceptions-1.2.tar.gz" } ], "1.3": [ { "comment_text": "", "digests": { "md5": "b884fc7f0d0861539153c3e279fe82fd", "sha256": "9071b5eb58fd10a11cce89bb28267e0f959744b17be929df36f35a40259021d2" }, "downloads": -1, "filename": "django-display-exceptions-1.3.tar.gz", "has_sig": false, "md5_digest": "b884fc7f0d0861539153c3e279fe82fd", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 144377, "upload_time": "2015-10-15T21:52:27", "url": "https://files.pythonhosted.org/packages/87/cb/476d8e37ecef4a2be163b91e27e1c7e2a8fefebd09a315af647745375005/django-display-exceptions-1.3.tar.gz" } ], "1.4": [ { "comment_text": "", "digests": { "md5": "23f42e9bd6eae6e42c696a0f097f4c07", "sha256": "d23861fad17407f9c093eb7ea8384ad39ab8dc80cd154ca079872df1fce0601e" }, "downloads": -1, "filename": "django-display-exceptions-1.4.tar.gz", "has_sig": false, "md5_digest": "23f42e9bd6eae6e42c696a0f097f4c07", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 144388, "upload_time": "2015-10-21T21:41:58", "url": "https://files.pythonhosted.org/packages/ca/7d/fce062448509718c4d6b64e8f74b420e06df6d32fe31ff1717ede86006a7/django-display-exceptions-1.4.tar.gz" } ], "1.5": [ { "comment_text": "", "digests": { "md5": "85aee0398b86b3580841503f85c98339", "sha256": "d5951c8447cae5e4e6b4518c8db49047b73fe8e1d454851f4e2b639b3764d13c" }, "downloads": -1, "filename": "django-display-exceptions-1.5.tar.gz", "has_sig": false, "md5_digest": "85aee0398b86b3580841503f85c98339", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 144457, "upload_time": "2015-10-21T21:45:57", "url": "https://files.pythonhosted.org/packages/7e/ca/dab5600f32600ac20a6456460bc6ca9e1863c29616e88d718603430c4136/django-display-exceptions-1.5.tar.gz" } ], "1.6": [ { "comment_text": "", "digests": { "md5": "3c342bf6785aea7d9915c5f6f052aa1c", "sha256": "3d5ad66e2625892e084fff9ac593685c05e31370bcaf9d7a70419103ebb3bb66" }, "downloads": -1, "filename": "django-display-exceptions-1.6.tar.gz", "has_sig": false, "md5_digest": "3c342bf6785aea7d9915c5f6f052aa1c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 144953, "upload_time": "2015-10-30T17:13:58", "url": "https://files.pythonhosted.org/packages/b4/b0/c7896619433162c4cfeac5f6dd5510ff54a62756bb0c2666317e783c080e/django-display-exceptions-1.6.tar.gz" } ], "1.7": [ { "comment_text": "", "digests": { "md5": "7808bcaa53c7de1f6b0ccf91cf7e9088", "sha256": "e7dbcf318d1e57273efd8f95a5e479c6e67b66293e6fd39a54e76a243ae18aa6" }, "downloads": -1, "filename": "django-display-exceptions-1.7.tar.gz", "has_sig": false, "md5_digest": "7808bcaa53c7de1f6b0ccf91cf7e9088", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 147646, "upload_time": "2016-02-20T19:34:23", "url": "https://files.pythonhosted.org/packages/28/7e/eebc68019507f609120f87d1c120f2fdc83ea22d4c12da1ad86926a1354c/django-display-exceptions-1.7.tar.gz" } ], "1.7.1": [ { "comment_text": "", "digests": { "md5": "8f665fda4437ed0c3fc2cfb18f90b6da", "sha256": "da566f1d20e8b7c5d7afd0b70450a82d8fe982d2640db7880064d025130d11e9" }, "downloads": -1, "filename": "django_display_exceptions-1.7.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "8f665fda4437ed0c3fc2cfb18f90b6da", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 149691, "upload_time": "2016-10-15T14:42:41", "url": "https://files.pythonhosted.org/packages/b4/b0/1fc4f39df8368408d04680e94c5ee7bb6f6c44f74a8c2b398d654a1e7a79/django_display_exceptions-1.7.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "6a6d2f436d249c372dacdb8135ca6e49", "sha256": "8e39998695606cbc56c2a62a8ffabd3f7d458b330d8aa31e609bb9403e016379" }, "downloads": -1, "filename": "django-display-exceptions-1.7.1.tar.gz", "has_sig": false, "md5_digest": "6a6d2f436d249c372dacdb8135ca6e49", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 147584, "upload_time": "2016-10-15T14:42:37", "url": "https://files.pythonhosted.org/packages/41/53/8c0d2c62c282fa86108d380cba9691361abfa122f146b3d0f1c2fcb5c14b/django-display-exceptions-1.7.1.tar.gz" } ], "1.8": [ { "comment_text": "", "digests": { "md5": "0e28ef4e645880951bf87a1c2918c3db", "sha256": "61a73db1184f93c543375b802fd315bf7549f7326187f343caad7b023bf60739" }, "downloads": -1, "filename": "django_display_exceptions-1.8-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "0e28ef4e645880951bf87a1c2918c3db", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 149764, "upload_time": "2017-01-05T14:51:03", "url": "https://files.pythonhosted.org/packages/8c/03/72803eccafa400bdae4c31b106f46484840b858373397a08494e649c8eef/django_display_exceptions-1.8-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "61cb52fb243f6b82670077a8588601ea", "sha256": "3c872fd57bc0d7077a8df50ee92d7007893fa0c660a6d5a7aaf94bc3236c1cfb" }, "downloads": -1, "filename": "django-display-exceptions-1.8.tar.gz", "has_sig": false, "md5_digest": "61cb52fb243f6b82670077a8588601ea", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 147651, "upload_time": "2017-01-05T14:51:00", "url": "https://files.pythonhosted.org/packages/be/24/64944cb6a54016d9bbe62232a116aff71fb972262adc7a8f691a431b43c1/django-display-exceptions-1.8.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "0e28ef4e645880951bf87a1c2918c3db", "sha256": "61a73db1184f93c543375b802fd315bf7549f7326187f343caad7b023bf60739" }, "downloads": -1, "filename": "django_display_exceptions-1.8-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "0e28ef4e645880951bf87a1c2918c3db", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 149764, "upload_time": "2017-01-05T14:51:03", "url": "https://files.pythonhosted.org/packages/8c/03/72803eccafa400bdae4c31b106f46484840b858373397a08494e649c8eef/django_display_exceptions-1.8-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "61cb52fb243f6b82670077a8588601ea", "sha256": "3c872fd57bc0d7077a8df50ee92d7007893fa0c660a6d5a7aaf94bc3236c1cfb" }, "downloads": -1, "filename": "django-display-exceptions-1.8.tar.gz", "has_sig": false, "md5_digest": "61cb52fb243f6b82670077a8588601ea", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 147651, "upload_time": "2017-01-05T14:51:00", "url": "https://files.pythonhosted.org/packages/be/24/64944cb6a54016d9bbe62232a116aff71fb972262adc7a8f691a431b43c1/django-display-exceptions-1.8.tar.gz" } ] }