{ "info": { "author": "Zhiwei Zhang", "author_email": "zhiwei2017@gmail.com", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3" ], "description": "## Introduction\n\n### Background\n\nIn a Django project, a lot of tables are used to display the information stored\n in DB. However, with the time goes by, the DB grows bigger and bigger, such \n that the table needs more time to load the content. In this case, reducing the \n loading time becomes the most import task for software developers. \n The common used way is using the server side processing table, which just loads\n a small part of the information at each time. \n\nTo use the server side processing table, we have two options: either we develop \na new one, or we just use the one implemented by someone else. For the first one,\n we have to invest a lot of time and energy, besides that we also make sure it \n can be used anywhere. That's pretty difficult and less interesting for me \n (thinking of the javascript code). For the second option, actually we just need\n to customize it. That's easy to achieve.\n\n[datatables](https://datatables.net/) is a javascript package, which provides \na lot of nice functions including the server side processing. However, using it \nin a Django project is not easy. To simplify its usage, I decided to implement \na small package.\n\n### Brief\n\n**sspdatatables** is a python package for Django project. It uses the \n[datatables](https://datatables.net/) package and provides a nice \nDjango-project-friendly interface for implementing the server side processing table.\n\nUsing this package you can implement not only a filterable, orderable and server\n side processing table. In common case, you don't even need to define the query \n function, I already did it for you.\n\n### Package Structure\n```\nsspdatatables\n| __init__.py\n| apps.py\n| datatables.py\n| forms.py\n|\n|---utils\n| | __init__.py\n| | decorator.py\n| | enum.py\n| | data_type_ensure.py\n|\n|---templates\n| |\n| |---datatables\n| | |\n| | |---html\n| | | table.html\n| | |\n| | |---js\n| | | footer.js\n| | | general.js\n| |\n|---templatetags\n | __init__.py\n | form_field.py\n|\n|---tests\n | __init__.py\n | test_data_type_ensure_doctest.txt\n | test_enum_doctest.txt\n```\n\n\n## How To Use\n\n### Setup\n\n1. run `pip install sspdatatables`\n2. add `sspdatatables` in the `INSTALLED_APPS` in **settings.py**\n3. add `sspdatatables/templates/` in the `DIRS` of `TEMPLATES` in **settings.py**\n\n### Tutorial\n\nIn this section I will use an example project to explain how to use **sspdatatables**\n\nProject **sspdatatablesExample**:\n\n*structure*:\n```\nsspdatatablesExample\n| manage.py\n| manuals.py\n| requirements.txt\n|\n|---example\n| | __init__.py\n| | admin.py\n| | apps.py\n| | models.py\n| | tests.py\n| | urls.py\n| | views.py\n| |\n| |---migrations\n| | | __init__.py\n| | | 0001_initial.py\n| |\n| |---templates\n| | index.html\n| | overview.html\n|\n|---sspdatatablesExample\n | __init__.py\n | admin.py\n | apps.py\n | settings.py\n | tests.py\n | urls.py\n | wsgi.py\n |\n |---migrations\n | __init__.py\n```\n\nContent of **models.py** file in **example** app\n```\nfrom django.db import models\nfrom django_countries.fields import CountryField\n\n\nclass Author(models.Model):\n name = models.CharField(max_length=60)\n nationality = CountryField()\n\n\nclass Book(models.Model):\n name = models.CharField(max_length=60)\n description = models.TextField()\n author = models.ForeignKey(Author, on_delete=None)\n published_at = models.DateField(auto_now=True)\n```\n\nWe want to display the *name*, *published_at*, *author_name*, \n*author_nationality* information of books.\n\nOur table should look like:\n\n| Action | Name | Author | Author Nationality | Published At |\n|------|----|------|------------------|------------|\n| :pencil: | book a | person 1 | country 1| Jul. 18, 2000|\n| :pencil: | book b | person 2 | country 2| Jul. 18, 1999|\n\n> ##### Notice:\n> * In datatables the column number is 0-index, which means the column *Action* is \nactually column **0**, column *Name* is column **1**\n\nAfter having a picture of what we want, we can start to implement it.\n\n#### Steps:\n##### Step 1.\ncreate a folder **datatables** in app **example** and add empty files:\n**__init__.py**, **datatables.py**, **enums.py**, **forms.py**, **serializers.py** in it\n> ###### Explaination:\n> * **sspdatatables** requisite files are normally not used by other files, so \nputting them together helps you to keep your project's structure clear and \nsimple (at least for the usage of **sspdatatables**).\n\n##### Step 2.\nin file **enums.py** input the following code:\n\n```python\nfrom sspdatatables.utils.enum import TripleEnum\n\n\nclass BookEnum(TripleEnum):\n ID = (1, \"id\", \"id\")\n NAME = (2, \"name\", \"name__icontains\")\n AUTHOR_NAME = (3, \"author__name\", \"author__name__icontains\")\n AUTHOR_NATIONALITY = (4, \"author__nationality\", \"author__nationality\")\n PUBLISHED_AT = (5, \"published_at\", \"published_at\")\n```\n> ###### Explaination:\n> * First, we need to import the `TripleEnum` from `sspdatatables`. Then we \ncreate a subclass for it to define a mapping, which is a 3-element tuple, (that's \nwhy the enumeration class called `TripleEnum`).\n> * The first element of the mapping means the **column number** in the table, \nthe second element is the corresponding **field name** defined in the model class,\n and the third element is the **filter key** for the field.\n> * The purpose of creating this enumeration class is that with knowing the \ncolumn number we can use the corresponding **field name** to order the table's \ncontent and the corresponding **filter key** to filter the content.\n\n##### Step 3.\nin file **serializers.py** input the following code:\n\n```python\nfrom example.models import Book, Author\nfrom rest_framework import serializers\nfrom django_countries.serializers import CountryFieldMixin\nfrom django_countries.serializer_fields import CountryField\n\n\nclass AuthorSerializer(CountryFieldMixin, serializers.ModelSerializer):\n nationality = CountryField(country_dict=True)\n\n class Meta:\n model = Author\n fields = ('name', 'nationality')\n\n\nclass BookSerializer(serializers.ModelSerializer):\n author = AuthorSerializer(read_only=True)\n published_at = serializers.DateField(format='%b. %d, %Y', required=False)\n\n class Meta:\n model = Book\n fields = ('id', 'name', 'published_at', 'author')\n```\n> ##### Explaination:\n> What we do here is actually defining the **ModelSerializer** for the table. \nThere are two reasons for creating two ModelSerializers (one for Author and one for Book):\n> 1. to access the foreign key referenced record's information, you have to \ndefine a **ModelSerializer** for it. That's defined by **Django REST framework**\n> 2. we want to display not only the book's information but also its author's \ninformation, and the author is referenced as a foreign key in book\n> ###### Notice:\n> * If you use **CountryField** in your model class and you want to display the \n**country name** instead of the **country code** in the table, you have to use \nthe same way as I defined for the **nationality** in the `AuthorSerializer` class\n (in the serialized data to get the country name, you can use `item.nationality.name`).\n> * If you want to display the date in a specific format, you have to use the \nsame name of this date field to declare a new `serializers.DateField` and specify the format \n(`serializers.DateField` corresponds to the `models.DateField` and `serializers.DateTimeField` corresponds to `models.DateTimeField`).\n\nFor more information about **Django REST framework**, you can visit [http://www.django-rest-framework.org/#tutorial](http://www.django-rest-framework.org/#tutorial)\n\n##### Step 4.\nin file **datatables.py** input the following code:\n\n```python\nfrom sspdatatables.datatables import DataTables\nfrom .serializers import BookSerializer\nfrom .enums import BookEnum\nfrom .forms import BookFieldSelectForm\n\n\nclass BookDataTables(DataTables):\n\n class Meta:\n serializer = BookSerializer\n form = None\n structure = [\n {\n \"id\": \"actions\", \"serializer_key\": None,\n \"header\": \"Actions\", \"searchable\": False,\n \"orderable\": False, \"footer_type\": None,\n },\n {\n \"id\": \"id\", \"serializer_key\": 'id',\n \"header\": \"ID\", \"searchable\": True,\n \"orderable\": True, \"footer_type\": \"input\",\n },\n {\n \"id\": \"name\", \"serializer_key\": 'name',\n \"header\": \"Name\", \"searchable\": True,\n \"orderable\": True, \"footer_type\": \"input\",\n },\n {\n \"id\": \"author\", \"serializer_key\": 'author.name',\n \"header\": \"Author\", \"searchable\": True,\n \"orderable\": True, \"footer_type\": \"input\",\n },\n {\n \"id\": \"author_nationality\", \"serializer_key\": 'author.nationality.name',\n \"header\": \"Author Nationality\", \"searchable\": True,\n \"orderable\": True, \"footer_type\": \"input\",\n },\n {\n \"id\": \"published_at\", \"serializer_key\": 'published_at',\n \"header\": \"Published At\", \"searchable\": True,\n \"orderable\": True, \"footer_type\": \"input\",\n \"placeholder\": \"YYYY-MM-DD\",\n },\n ]\n col_triple_enum = BookEnum\n\n```\n> ###### Explaination:\n> * The class `DataTables` works as a bridge to connect the display table and the DB:\n> * configures the table's frame according to its internal variable `structure`\n> * serializes the records from DB for displaying (variable `serializer`)\n> * applies the filter condition provided by the table to the DB with the help \nof its internal variable `col_triple_enum`\n> * customizes the table footer's search field to *select* field instead of the default *input* field (variable `form`)\n> * The usage of `DataTables` is quite simple, you just need to create a subclass \nfor it and define its internal Meta class's variables.\n> * So far we just want to have a table with footer search field (in form of *input*). \nAt last, I will show you how to change the footer search field to *select* type.\n> ###### Notice:\n> * If your table has a complicated query and you want to customize the query \nfunction, you can redefine the functions `get_query_dict`, `get_order_key`, \n`filtering`, `slicing`, `filter_by_args` or `process` inside your defined \nsubclass of `DataTables` as you need. In this case you need to read the source code first.\n\n##### Step 5.\nSo far we already set up the backend part for using `sspdatatables`. In this step \nwe start to deal with the frontend part.\n\nTwo views functions are needed: one is for rendering the table's frame, another \none is for loading the table's content. \n\nFirstly, in file **view.py** of the app *example* create the first views function \nfor displaying the tables' frame:\n\n```python\nfrom django.shortcuts import render\nfrom django.http import JsonResponse\nfrom .datatables import BookDataTables\nfrom sspdatatables.utils.decorator import ensure_ajax\nfrom collections import OrderedDict\n\ndef overview(request):\n book_datatables = BookDataTables()\n context = book_datatables.get_dt_elements()\n context.update({\n \"title\": \"Books\",\n })\n return render(request, 'overview.html', context)\n```\n> ###### Explaination:\n> * through the function `get_dt_elements` you can get the frame (the return \nresult of function `get_dt_elements` is a dictionary containing just one \nkey-value pair), and you can also extend the `context` in case you want to \nrender more variables\n\nSecondly, still in this file create the second views function for loading the content:\n```python\n@ensure_ajax(['GET'])\ndef get_book_api(request):\n pre_search_condition = OrderedDict([('select_related', 'author')])\n book_datatables = BookDataTables()\n result = book_datatables.process(pre_search_condition=pre_search_condition,\n **request.GET)\n return JsonResponse(result)\n```\n> ###### Explaination:\n> * through the decorator `ensure_ajax` we can make sure only the ajax request \nwith allowed request method(s) is accepted by the views function. \n> * the `pre_search_condition` parameter of the function `process` must be an \n**OrderedDict** type:\n - The key in `pre_search_condition` is the function name in the *ModelManager* \n of the model class 'Book', such as `selected_related`, `filter`, `values` etc. \n - The value in in `pre_search_condition` is the corresponding function's parameters' \n values. For example:\n + if you use `select_related` as the key, you can input a single value as \n the related DB table or a list of values. \n + if you use `filter` as the key, its value must be a dictionary, in \n which a key-value pair means a *filter key* and its corresponding *filter value*, \n such as `OrderedDict([('filter', {'name__icontains', 'Django'})])`\n> * the `DataTables` class gonna iterate the `pre_search_condition` and apply \nthose conditions by order.\n> * since the `author` is a foreign key in `Book` model class, we need to set the \n`select_related` in `pre_search_condition`\n\nThirdly, define the urls for both views functions like:\n```python\nfrom django.conf.urls import url\nfrom example.views import overview, get_book_api\n\n\nurlpatterns = [\n url(r'^books/$', overview, name='book_overview'),\n url(r'^api/$', get_book_api, name='book_api'),\n]\n```\n\n##### Step 6.\nIn this step we gonna write some *html* code and *javascript* code.\n\n1. prepare a html file for the table, in `sspdatatablesExample` project I create\n a html file called `overview.html` in app `example`'s `templates` folder. \n It looks like as following:\n\n ```html\n {% extends \"index.html\" %}\n\n {% load static %}\n\n {% block content %}\n {% endblock %}\n\n\n {% block scripts %}\n {% endblock %}\n ```\n\n2. in the `block content` include the `'datatables/html/table.html` file from \n`sspdatatables`, and in the `block scripts` include `datatables/js/general.js` \nfrom `sspdatatables`.Now the `overview.html` should be like:\n\n ```html\n {% extends \"index.html\" %}\n\n {% load static %}\n\n {% block content %}\n
\n
\n {% include 'datatables/html/table.html' %}\n
\n
\n {% endblock %}\n\n\n {% block scripts %}\n {% include 'datatables/js/general.js' %}\n // using passed sspdatatables variable in context to replace the footer with input field or select field\n {% include 'datatables/js/footer.js' %}\n {% endblock %}\n ```\n\n3. in the `block scripts` declare an instance of `DataTable` with the settings \nyou want (here I will provide some common used settings). The `overview.html` becomes:\n\n ```html\n {% extends \"index.html\" %}\n\n {% load static %}\n\n {% block content %}\n
\n
\n {% include 'datatables/html/table.html' %}\n {% csrf_token %}\n
\n
\n {% endblock %}\n\n\n {% block scripts %}\n {% include 'datatables/js/general.html' %}\n // using passed sspdatatables variable in context to replace the footer with input field or select field\n {% include 'datatables/js/footer.js' %}\n\n \n {% endblock %}\n ```\n\nYou can read the documentation in `DataTable` website to know about the javascript settings.\n\nAt this point, we already implement out table for the model class `Book`. If you \nwant to know how to customize the footer search field, you can go the next step.\n\n##### Step 7.\nin the file `forms.py` in folder `datatables` of app `example` input the following code:\n\n```python\nfrom sspdatatables.forms import AbstractFooterForm\nfrom django.forms import ChoiceField\nfrom django_countries import countries\n\n\nclass BookFieldSelectForm(AbstractFooterForm):\n def get_author_nationality_choices(self):\n return [(None, 'Select')] + list(countries.countries.items())\n\n class Meta:\n fields = [(\"author_nationality\", ChoiceField),]\n\n```\n> ###### Explaination:\n> in `sspdatatables` there is a form class called `AbstractFooterForm`, which \nsimplify the definition of the form class for the footer search field. What you need to do is\n> 1. in the inside `Meta` class define the variable `fields` as a list of 2-element tuples: \n> - first element is the **id** of the column (defined in the variable \n`structure` of class `BookDataTables` in `datatables.py` file)\n> - the second element is its corresponding footer field type (currently \nit just supports `ChoiceField`).; \n> 2. define a function for every element in `fields`. The name of the function\n must be `get__choices`. And this function return \n the choices for this select field in form of list of tuples.\n\nAfter that, there are only two small steps remaining, go to the `datatables.py` file\n1. set the value of variable `form` of class `BookDataTables` to the form class \nwe just defined `BookFieldSelectForm`\n2. find the corresponding column's frame definition in variable `structure` of \nclass `BookDataTables`, change the value of key `footer_type` to `'select'`\n\nThe `datatables.py` file should be:\n\n```python\nfrom sspdatatables.datatables import DataTables\nfrom .serializers import BookSerializer\nfrom .enums import BookEnum\nfrom .forms import BookFieldSelectForm\n\n\nclass BookDataTables(DataTables):\n\n class Meta:\n serializer = BookSerializer\n form = BookFieldSelectForm\n structure = [\n {\n \"id\": \"actions\", \"serializer_key\": None,\n \"header\": \"Actions\", \"searchable\": False,\n \"orderable\": False, \"footer_type\": None,\n },\n {\n \"id\": \"id\", \"serializer_key\": 'id',\n \"header\": \"ID\", \"searchable\": True,\n \"orderable\": True, \"footer_type\": \"input\",\n },\n {\n \"id\": \"name\", \"serializer_key\": 'name',\n \"header\": \"Name\", \"searchable\": True,\n \"orderable\": True, \"footer_type\": \"input\",\n },\n {\n \"id\": \"author\", \"serializer_key\": 'author.name',\n \"header\": \"Author\", \"searchable\": True,\n \"orderable\": True, \"footer_type\": \"input\",\n },\n {\n \"id\": \"author_nationality\", \"serializer_key\": 'author.nationality.name',\n \"header\": \"Author Nationality\", \"searchable\": True,\n \"orderable\": True, \"footer_type\": \"select\",\n },\n {\n \"id\": \"published_at\", \"serializer_key\": 'published_at',\n \"header\": \"Published At\", \"searchable\": True,\n \"orderable\": True, \"footer_type\": \"input\",\n \"placeholder\": \"YYYY-MM-DD\",\n },\n ]\n col_triple_enum = BookEnum\n```\n\nEnjoy!\n\nThe source code of `sspdatatablesExample` is included in the package's *example* folder.\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/zhiwei2017/sspdatatables", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "sspdatatables", "package_url": "https://pypi.org/project/sspdatatables/", "platform": "", "project_url": "https://pypi.org/project/sspdatatables/", "project_urls": { "Homepage": "https://github.com/zhiwei2017/sspdatatables" }, "release_url": "https://pypi.org/project/sspdatatables/0.1.1/", "requires_dist": [ "pytz", "django", "djangorestframework" ], "requires_python": "", "summary": "Django package serverside processing datatables", "version": "0.1.1" }, "last_serial": 4461678, "releases": { "0.1.1": [ { "comment_text": "", "digests": { "md5": "cacd3a68ebfc7b52d3f6fbe2d1b827de", "sha256": "de2d847bd53eba866abea40ef1ca5785055d0434e8c2b5e3d84b8c05f429a52c" }, "downloads": -1, "filename": "sspdatatables-0.1.1-py3-none-any.whl", "has_sig": false, "md5_digest": "cacd3a68ebfc7b52d3f6fbe2d1b827de", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 20708, "upload_time": "2018-11-07T14:24:25", "url": "https://files.pythonhosted.org/packages/f2/ab/0a522d5a2d68cc138258ebfa509e00c7d50a345b61e0f2f8e117c0c6f2f3/sspdatatables-0.1.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "fdb6ca5af0362b0a6ec66231cee35d5b", "sha256": "47281bd6d71dc9787305e79dd73ce0a7a0436ed0d7410202df8ec4580e234930" }, "downloads": -1, "filename": "sspdatatables-0.1.1.tar.gz", "has_sig": false, "md5_digest": "fdb6ca5af0362b0a6ec66231cee35d5b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 24552, "upload_time": "2018-11-07T14:24:27", "url": "https://files.pythonhosted.org/packages/2a/a0/bb2b110a21480412edd8c1e54acb3cb4cde05919febe88b025097fc92afc/sspdatatables-0.1.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "cacd3a68ebfc7b52d3f6fbe2d1b827de", "sha256": "de2d847bd53eba866abea40ef1ca5785055d0434e8c2b5e3d84b8c05f429a52c" }, "downloads": -1, "filename": "sspdatatables-0.1.1-py3-none-any.whl", "has_sig": false, "md5_digest": "cacd3a68ebfc7b52d3f6fbe2d1b827de", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 20708, "upload_time": "2018-11-07T14:24:25", "url": "https://files.pythonhosted.org/packages/f2/ab/0a522d5a2d68cc138258ebfa509e00c7d50a345b61e0f2f8e117c0c6f2f3/sspdatatables-0.1.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "fdb6ca5af0362b0a6ec66231cee35d5b", "sha256": "47281bd6d71dc9787305e79dd73ce0a7a0436ed0d7410202df8ec4580e234930" }, "downloads": -1, "filename": "sspdatatables-0.1.1.tar.gz", "has_sig": false, "md5_digest": "fdb6ca5af0362b0a6ec66231cee35d5b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 24552, "upload_time": "2018-11-07T14:24:27", "url": "https://files.pythonhosted.org/packages/2a/a0/bb2b110a21480412edd8c1e54acb3cb4cde05919febe88b025097fc92afc/sspdatatables-0.1.1.tar.gz" } ] }