{ "info": { "author": "Tim Lauv", "author_email": "bluekvirus@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Environment :: Web Environment", "Framework :: Django", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Software Development :: Build Tools" ], "description": "django-express\n==============\n\n|PyPI-v| |PyPI-pyv| |PypI-djangov|\n\nEasy Restful APIs with the Django web framework.\n\nInstall\n-------\n\nDownload through ``pip`` (virtualenv -p python3 .venv)\n\n::\n\n pip install django-express\n\nAdd it to your ``INSTALLED_APPS`` in ``settings.py``\n\n::\n\n INSTALLED_APPS = [\n # ...\n 'django.contrib.staticfiles',\n 'express',\n ]\n\nSetup\n-----\n\nMount the auto-discovered services to any entry point (url) you want in\n``urlpatterns``\n\n::\n\n # proj/proj/urls.py\n\n from django.conf.urls import url, include\n from express import services\n\n urlpatterns = [\n url(r'^api/v1/', include(services.urls)) # mount everything\n url(r'^app-name/api/v1/', include(services.url('app-name', ...))) # mount only those from specific app(s)\n ]\n\nPlease **double check** if your ``url()`` call here has the path\nargument **ending with a trailing slash** (e.g ``foo/bar/``). This is\nrequired by the Django framework. You do not need to have this in your\n``@url()`` decorator paths though.\n\nStart serving apis\n------------------\n\nYou can just start Django like normal, your apis will be automatically\ndiscovered and mounted.\n\n::\n\n ./manage.py runserver 0.0.0.0:8000\n\nNote that for other developers to use your apis, you need to bind on\nwildcard or public WAN/LAN accessable IP address specifically after\n``runserver`` instead of leaving the param out to use the default\n``127.0.0.1`` localhost IP. If you are developing inside a VM (e.g\nthrough our *Vagrant* web dev vm) it is very important that you specify\nthe VM's IP or the wildcard IP after ``runserver`` so that you can use\nyour host machine's browser for accessing the apis through vm to host\nforwarded ports.\n\nAlso, use ``runserver`` with ``DEBUG=true`` in ``settings.py`` will\nautomatically serve all the ``static/`` sub-folders (and those added by\n``STATICFILES_DIRS``) from your apps. They are served like they were\nmerged under the same uri set by ``STATIC_URL`` in your ``settings.py``,\nso if you do not want files from different apps to override each other,\nput all your static assets (e.g \\*.js/html/css/png) in a sub-folder with\nthe same name as your app inside each ``static/``. Though ``STATIC_URL``\ndoesn't have a default value, after running\n``django-admin startproject`` your ``settings.py`` will set a default\nvalue ``/static/`` to it so you could access files from the ``static/``\nsub-folders under ``http://domain:8000/static/`` with zero\nsetup time.\n\nIf you are not using the ``runserver`` command for serving static assets\nand service apis during development, make sure you call\n``./manage.py collectstatics`` and serve folder ``STATIC_ROOT`` on\n``STATIC_URL``, so that ``{% load static %}`` then\n``{% static \"images/hi.jpg\" %}`` can work properly in your templates.\n\nAdding RESTful services\n-----------------------\n\nCreate apps in your Django project **normally**, this is to sub-divide\nyour services by app name for better maintainability. Optional though.\n\n::\n\n ./manage.py startapp app_example\n ./manage.py startapp another_app_with_services\n\nFunction as service api\n~~~~~~~~~~~~~~~~~~~~~~~\n\nAdd a ``services.py`` file in each app folder containing the service\nfunctions ``fn(req, res, *args, **kwargs)`` decorated with ``@service``\n\n::\n\n # proj/app_example/services.py\n from express.decorators import service, methods, url\n\n\n # /api/v1/absolute/url\n # /api/v1/app_example/relative/abcd\n\n @methods('GET', 'POST')\n @url('/absolute/url')\n @url('relative/abcd')\n @service\n def abc(req, res, *args, **kwargs):\n res.json({'json': req.json, 'link:': reverse('express:testa.abc')})\n\n\n # /api/v1/app_example/efg\n\n @service\n def efg(req, res, *args, **kwargs):\n res.json({\n 'params': dict(req.params.lists()), # use {**req.params} in python 3.5+\n 'form': dict(req.form.lists()), # use {**req.form} in python 3.5+\n 'json': req.json, \n 'mime': req['CONTENT_TYPE'],\n })\n\n\n # /api/v1/app_example/hij\n\n @service\n def hij(req, res, *args, **kwargs):\n res.file('db.sqlite3')\n\n\n # /api/v1/app_example/x\n\n @service\n def x(req, res, *args, **kwargs):\n #res.text('Nothing but a test from {}'.format(__name__))\n res.text('

Agent: {}

'.format(req['HTTP_USER_AGENT']))\n res.html('

IP: {}

'.format(req['REMOTE_ADDR']))\n res.text('

Method: {}

'.format(req['REQUEST_METHOD']))\n\n\n # /api/v1/app_example/relative/url/y-service/articles/2017/01/\n\n @url('relative/url/y-service/articles/([0-9]{4})/([0-9]{2})/')\n @service\n def y1(req, res, y, m, *args, **kwargs):\n res.json({\n 'data': 'Nothing but a test from {}.{}'.format(__name__, 'y1 - positional capture'),\n 'text': 123,\n 'year': y,\n 'month': m,\n })\n res.header('Hello~', 'World!') # header\n res.status(201) # status\n\n\n # /api/v1/app_example/z\n\n @service\n def z(req, res, *args, **kwargs):\n res.download('db.sqlite3')\n\nAs you can see, you can still use regex captures in ``@url('..path..')``\nif prefered. The captured group/named group will be passed normally to\nyour service function as positional args and keyword args. However,\n**You can NOT use both positioned and namged group captures in the same\nurl!! Due to django implementation.**\n\nImportant Note\n^^^^^^^^^^^^^^\n\nPut ``@service`` as the inner-most decorator, other decorators don't\nhave this hard requirement on ordering here. You can still use all the\ndecorators from the Django web framework like ``@permission_required``\nor ``@login_required`` but make sure they are all above ``@service``.\n\nArgument APIs\n^^^^^^^^^^^^^\n\nThe most important arguments to your service function would be the first\ntwo, namely ``req`` for request and ``res`` for response. Here are the\navailable methods on these two objects.\n\nreq (ExpressRequest)\n''''''''''''''''''''\n\n- req.params['key']\n- req.json\n- req.form\n- req.files['name']\n- req.cookies['name']\n- req['HTTP-HEADER']/req.header('key')\n\nres (ExpressResponse)\n'''''''''''''''''''''\n\n- res.redirect('url')\n- res.render(req, 'template', context={})\n- res.html('str')/text('str')\n- res.json(dict)\n- res.file('path')\n- res.attach('path')/download('path')\n- res.status(int)\n- res['HTTP\\_HEADER']/res.header('key', val)\n\n**Caveat:** ``res.status()`` and ``res['HTTP_HEADER']/res.header()``\nmust be called after\n``.render()/html()/text()/json()/file()/attach()/download()`` in your\nservice function for new headers and status to be applied to the\nresponse.\n\nModel generated service apis\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWithin the ``models.py`` file, you can decorate any of your Model class\ndirectly for it to generate the apis around its CRUD database\noperations.\n\n::\n\n # proj/app_example/models.py\n\n @url('/absolute/db/device')\n @url('db/device')\n @serve_unprotected\n class Device(models.Model):\n \"\"\"docstring for Device\"\"\"\n sn = models.CharField(max_length=32)\n\nThis will mount 5 default service functions bound to different HTTP\nmethods (POST/GET/PUT,PATCH/DELETE/HEAD) to url\n``app_example/models/Device`` for its CRUD database operations and one\nmore metadata operations.\n\nDecorators\n----------\n\nFor a function\n~~~~~~~~~~~~~~\n\n@service\n^^^^^^^^\n\nTurn your ``fn(req, res, *args, **kwargs)`` function into a Restful\nservice routine. Automatically detected if present in ``services.py`` in\nany **installed** app.\n\n- Default path with ``services.urls``: ``//``\n- Default path with ``services.url(app, noprefix=True)``: ``/``\n\nYou can change the mounting path by using the ``@url()`` decorator. You\ncan also use ``django.urls.reverse()`` to get the mount point by name\n``:.services.`` **if you mount the\n``services.url(s)`` with namespaced ``include()`` calls in\n``urls.py``**.\n\nStill, **do not forget** to mount everthing collected inside\n``services.urls`` to a root url in the django ``urls.py``. See the\n**Setup** section above.\n\n@methods(m1, m2, ...)\n^^^^^^^^^^^^^^^^^^^^^\n\nAllowed HTTP request methods to the service. You can also use ``@safe``\nto allow only ``GET`` and ``HEAD`` requests. You can use different\n``@methods()`` on each service function with the same ``@url()`` path to\nreuse the same url.\n\n@url(path)\n^^^^^^^^^^\n\nOverride basic service auto-path (``//``). No need to use\n``r'..path..'`` here, what you put in ``path`` will be treated as raw\nstring automatically. Feel free to put regex group captures. **Just\ndon't mix named and annonymous capture groups in the url path, they\nwon't work together in django.**\n\nYou can use multiple ``@url()`` on the same service function.\n\n@csrf\n^^^^^\n\nSetting CSRF token cookie on ``GET/HEAD`` requests to the service.\nChecks and rejects ``POST/PUT/PATCH/DELETE`` requests according to their\ncsrf token + cookie pairs.\n\nIf you want an Ajax request to be guarded by django CSRF\n(django.middleware.csrf.CsrfViewMiddleware) you need to ``GET/HEAD`` the\n``@csrf`` decorated service first to have your CSRF cookie (named\n``csrftoken``) set, then ``POST/PUT/PATCH/DELETE`` to it with real\nrequests including either ``X-CSRFToken`` in header or\n``csrfmiddlewaretoken`` in a hidden form ```` field. The header\nor hidden field value should match the value given by the cookie.\n\nYou can change the cookie and header names but **NOT** the hidden field\nname in the django ``settings.py``.\n\nFor a Model\n~~~~~~~~~~~\n\n@serve\n^^^^^^\n\nGive a Model default RESTful apis to its CRUD operations.\n\n- Default path with ``services.urls``: ``//``\n- Default path wiht ``services.url(app, noprefix=True)``: ``/``\n\nYou can change the mounting path by using the ``@url()`` decorator. You\ncan also use ``django.urls.reverse()`` to get the mount point by name\n``:.models.``.\n\n- POST -- create -- {\"payload\": {...data...}}\n- GET -- read -- ?pk= for single record, omit for all\n- PUT/PATCH -- update -- {\"payload\": {\"id\": \"...\", ...data...}}\n- DELETE -- delete -- ?pk= for target record, required\n- HEAD -- meta -- model name ``X-Django-App-Model`` and table count\n ``X-DB-Table-Count`` in reply headers\n\nWhen using **GET** http request on a ``@serve``\\ (-ed) model, you can\nalso specify params for filtering (by columns and Django ORM filter\noperations), sorting (by columns) and paging the returned result.\n\n::\n\n ?filter=foo1:op_and_val1&filter=foo2:op_and_val2\n ?sort=foo, -bar\n\n ?size=number\n ?offset=number\n ?page=number\n\nWhen using **Any** http requests on a ``@serve``\\ (-ed) model, you can\nalways use ``?db=...`` to switch onto the specific database for served\nmodel apis to query and modify. The database names come from your\n``DATABASES`` configure in ``settings.py``.\n\nStill, **do not forget** to mount everthing collected inside\n``services.urls`` to a root url in the django ``urls.py``. See the\n**Setup** section above.\n\n@serve\\_unprotected\n^^^^^^^^^^^^^^^^^^^\n\nSame as @serve but without csrf protection.\n\n@methods(m1, m2, ...)\n^^^^^^^^^^^^^^^^^^^^^\n\nSame as @methods for a service function.\n\n@url(path)\n^^^^^^^^^^\n\nSame as @url for a service function.\n\nDatabase Backends\n-----------------\n\nbackends.mongodb\n~~~~~~~~~~~~~~~~\n\nThis is a dummy backend engine to use with MongoDB connections without\nthe involvement of Django ORM. The purpose is to have your MongoDB\nsettings in the ``settings.py`` and use\n``django.db.connections['']`` to start using MongoDB\nin your Django apps.\n\n::\n\n # settings.py\n\n DATABASES = {\n ...,\n 'mongo': {\n 'ENGINE': 'express.db.backends.mongodb',\n 'HOST': 'mongo.server.com',\n 'PORT': 27017,\n 'NAME': 'testdb',\n 'USER': '...',\n 'PASSWORD': '...',\n 'OPTIONS': {\n ...pymongo.MongoClient options...\n }\n },\n ...\n }\n\nNow you will have,\n\n- django.db.connections['testdb'].db - a ``pymongo`` db object;\n- django.db.connections['testdb'].collection('collection'=None) - a\n ``pymongo`` collection or all available collection names;\n- django.db.connections['testdb'].cursor('collection', \\*\\*kwargs) - a\n .find(kwargs) ``pymongo`` cursor;\n\nAfter getting the above, you will have, \\*\ndjango.db.connections['testdb'].connection - a ``pymongo`` client;\n\nUse ``.cursor()`` for search (``GET``) apis and ``.collection()`` for\nmodify (``POST/PUT/PATCH/DELETE``) apis.\n\nLimitation\n^^^^^^^^^^\n\nThis engine works up to the point of creating the db connection and\ncollection cursor, taking in DATABASES options from your settings.py;\nThe ORM layer (migration, schema, transactions, save/delete()) will not\nwork on database that has settings using this Engine.\n\nLicence\n-------\n\nCopyright 2017 Tim Lauv. Under the\n`MIT `__ License.\n\n.. |PyPI-v| image:: https://img.shields.io/pypi/v/django-express.svg\n :target: https://pypi.python.org/pypi/django-express\n.. |PyPI-pyv| image:: https://img.shields.io/pypi/pyversions/django-express.svg\n :target: https://pypi.python.org/pypi/django-express\n.. |PypI-djangov| image:: https://img.shields.io/badge/Django-1.8%2C%201.9%2C%201.10-44B78B.svg\n :target: https://www.djangoproject.com/\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/bluekvirus/django-express", "keywords": "django,restful api,express,services", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "django-express", "package_url": "https://pypi.org/project/django-express/", "platform": "", "project_url": "https://pypi.org/project/django-express/", "project_urls": { "Homepage": "https://github.com/bluekvirus/django-express" }, "release_url": "https://pypi.org/project/django-express/0.4.0/", "requires_dist": [ "Django (>=1.11)", "pymongo (>=3.4); extra == 'pymongo'" ], "requires_python": "", "summary": "Easy Restful APIs with the Django web framework.", "version": "0.4.0" }, "last_serial": 3712205, "releases": { "0.2.2": [ { "comment_text": "", "digests": { "md5": "f162758bf1024907d8cf2c8292a20d89", "sha256": "22999504676557a765b7adf40514a8f96bf1fe0da83846a0c4b4e864b6653e9f" }, "downloads": -1, "filename": "django_express-0.2.2-py3-none-any.whl", "has_sig": false, "md5_digest": "f162758bf1024907d8cf2c8292a20d89", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 11041, "upload_time": "2017-01-22T06:22:25", "url": "https://files.pythonhosted.org/packages/7c/65/5641cfc7b5461b7f58f5303f503c93c04ece81f18c54e9c9c6e7b5145117/django_express-0.2.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "143e86e526dd1bd53640ea19f16cb607", "sha256": "b3d23c49581c772151aaa8e8cfb1e78dc0ba256ebb418e458a3e6a0bb3e4ec68" }, "downloads": -1, "filename": "django-express-0.2.2.tar.gz", "has_sig": false, "md5_digest": "143e86e526dd1bd53640ea19f16cb607", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8889, "upload_time": "2017-01-22T06:22:26", "url": "https://files.pythonhosted.org/packages/63/2a/986bbf13cadc719212da46a854f6ed0ca4c9ccb98a2afa73463efe2fb6b9/django-express-0.2.2.tar.gz" } ], "0.2.5": [ { "comment_text": "", "digests": { "md5": "21a1c8356d319c21711a70ae13655685", "sha256": "7ff9a06bb1e86cfab331365a7e3ce2c5df322307f9f7f55014a308307e117249" }, "downloads": -1, "filename": "django_express-0.2.5-py3-none-any.whl", "has_sig": false, "md5_digest": "21a1c8356d319c21711a70ae13655685", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 12084, "upload_time": "2017-01-30T07:23:35", "url": "https://files.pythonhosted.org/packages/02/a5/92425761f28ea30070e499a33ea32226d36dbac1b41d92789de9b9f1f41c/django_express-0.2.5-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "a0a4ba5a889978cdb63419b6080a109c", "sha256": "2f8abdb2456ff56e605e0614fcecb6f958e02850bf1b818554446947270a39f3" }, "downloads": -1, "filename": "django-express-0.2.5.tar.gz", "has_sig": false, "md5_digest": "a0a4ba5a889978cdb63419b6080a109c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9509, "upload_time": "2017-01-30T07:23:36", "url": "https://files.pythonhosted.org/packages/47/e4/b7f2b01f41e9d89294dcc041df576a297d080552b2750598190039388883/django-express-0.2.5.tar.gz" } ], "0.3.1.dev6": [ { "comment_text": "", "digests": { "md5": "fb9f9baa2f2d8c4832ca289ad594bf0c", "sha256": "519e6235c63c3300c50b7f031aa1b67b4dbf4fb42e95accfda51092a8603e1ac" }, "downloads": -1, "filename": "django_express-0.3.1.dev6-py3-none-any.whl", "has_sig": false, "md5_digest": "fb9f9baa2f2d8c4832ca289ad594bf0c", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 26094, "upload_time": "2017-03-14T21:13:43", "url": "https://files.pythonhosted.org/packages/b1/57/338fe426c2a81b884437e2127fc32a2aadae25f7cccaa582df3baf476635/django_express-0.3.1.dev6-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "faca28081d963fda742c97e4e28d10cc", "sha256": "e157d9f918cb6e2ae0382e1024aa21f274188bff0e5b577802b76d67efb59ebe" }, "downloads": -1, "filename": "django-express-0.3.1.dev6.tar.gz", "has_sig": false, "md5_digest": "faca28081d963fda742c97e4e28d10cc", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20679, "upload_time": "2017-03-14T21:13:45", "url": "https://files.pythonhosted.org/packages/ee/a1/b14933b968d9a9ee68993647ea8745946c622aca6a4fd03e8dbe741edca0/django-express-0.3.1.dev6.tar.gz" } ], "0.3.2": [ { "comment_text": "", "digests": { "md5": "b1808f9114a36c0a987fda0eb69b080b", "sha256": "15a6c150d4eac45982ec9506760a0b0eafa6df30c3cec7abc5fc9e73d6d94688" }, "downloads": -1, "filename": "django_express-0.3.2-py3-none-any.whl", "has_sig": false, "md5_digest": "b1808f9114a36c0a987fda0eb69b080b", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 28139, "upload_time": "2017-03-25T03:43:40", "url": "https://files.pythonhosted.org/packages/be/13/3ee8128d1c5fb5932e17738a73c1207a8e4f1928081b8addde51c0b19f4a/django_express-0.3.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "cb60b0fbdd940768c3abd67eb8280ca4", "sha256": "066c16e332ccb3bdc9c179b3a2f1bb84c385f94d08826a8017e0660b7e012d86" }, "downloads": -1, "filename": "django-express-0.3.2.tar.gz", "has_sig": false, "md5_digest": "cb60b0fbdd940768c3abd67eb8280ca4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21809, "upload_time": "2017-03-25T03:43:41", "url": "https://files.pythonhosted.org/packages/2f/1f/a844d6bc085c135383816e179d8b7cc057da67e4a1ffd73336805450c064/django-express-0.3.2.tar.gz" } ], "0.3.3.dev1": [ { "comment_text": "", "digests": { "md5": "4663587b8ab4b97e3aa20c847f02ece9", "sha256": "a505260bd96e700105096dd6af4a26eeedca161cd423f35319d504cbbaafc7ff" }, "downloads": -1, "filename": "django_express-0.3.3.dev1-py3-none-any.whl", "has_sig": false, "md5_digest": "4663587b8ab4b97e3aa20c847f02ece9", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 29575, "upload_time": "2017-04-30T08:04:31", "url": "https://files.pythonhosted.org/packages/6d/7f/46d49f6769cea8158c1264eefda626919f4b55f01315151dea694cb7ec29/django_express-0.3.3.dev1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "f0edeb4a926e05318d1604cc90e464af", "sha256": "bcabce847d73c7f97525c48704dc5f991acadf832087b5e80e042c666ca4555b" }, "downloads": -1, "filename": "django-express-0.3.3.dev1.tar.gz", "has_sig": false, "md5_digest": "f0edeb4a926e05318d1604cc90e464af", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22359, "upload_time": "2017-04-30T08:04:32", "url": "https://files.pythonhosted.org/packages/a4/f6/54983ec5b62321cf3725358adf4ec59f9a014ce00ab0858f4c08fb5bb312/django-express-0.3.3.dev1.tar.gz" } ], "0.4.0": [ { "comment_text": "", "digests": { "md5": "8247aa8044a00b595131f414b41b8337", "sha256": "efb9804bb60a8cb8c0902b14e365f03cd22defbeff7394687c668fdaf5dfc6bb" }, "downloads": -1, "filename": "django_express-0.4.0-py3-none-any.whl", "has_sig": false, "md5_digest": "8247aa8044a00b595131f414b41b8337", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 29525, "upload_time": "2018-03-27T23:52:41", "url": "https://files.pythonhosted.org/packages/ff/a1/b4f629cb175782ed7f0dd16f46a483f44f4f567748839db01baf12e89e43/django_express-0.4.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "4316a4c19f5515d08bef17ccabc094c3", "sha256": "b947b09da39661cd055248965c4ebfea8f3b3d21750fa9cb4c14cfca6b12cf9d" }, "downloads": -1, "filename": "django-express-0.4.0.tar.gz", "has_sig": false, "md5_digest": "4316a4c19f5515d08bef17ccabc094c3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19514, "upload_time": "2018-03-27T23:52:43", "url": "https://files.pythonhosted.org/packages/0f/89/45bf9ff2e366571caf1b3ada90a853ac086e5b741861228f22a8b6a29d96/django-express-0.4.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "8247aa8044a00b595131f414b41b8337", "sha256": "efb9804bb60a8cb8c0902b14e365f03cd22defbeff7394687c668fdaf5dfc6bb" }, "downloads": -1, "filename": "django_express-0.4.0-py3-none-any.whl", "has_sig": false, "md5_digest": "8247aa8044a00b595131f414b41b8337", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 29525, "upload_time": "2018-03-27T23:52:41", "url": "https://files.pythonhosted.org/packages/ff/a1/b4f629cb175782ed7f0dd16f46a483f44f4f567748839db01baf12e89e43/django_express-0.4.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "4316a4c19f5515d08bef17ccabc094c3", "sha256": "b947b09da39661cd055248965c4ebfea8f3b3d21750fa9cb4c14cfca6b12cf9d" }, "downloads": -1, "filename": "django-express-0.4.0.tar.gz", "has_sig": false, "md5_digest": "4316a4c19f5515d08bef17ccabc094c3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19514, "upload_time": "2018-03-27T23:52:43", "url": "https://files.pythonhosted.org/packages/0f/89/45bf9ff2e366571caf1b3ada90a853ac086e5b741861228f22a8b6a29d96/django-express-0.4.0.tar.gz" } ] }