13 | from datatableview import columns
14 | from datatableview.datatables import Datatable
15 | from myproject.fields import CustomField
16 |
17 | class CustomColumn(columns.Column):
18 | model_field_class = CustomField
19 | lookup_types = ['icontains', 'mycustomquery']
20 |
21 |
22 | # You can let the field detect the new column by itself:
23 | class MyDatatable(Datatable):
24 | class Meta:
25 | columns = ['my_customfield']
26 |
27 | # Or you can always use it explicitly:
28 | class MyDatatable(Datatable):
29 | customcolumn = CustomColumn("My Field", sources=['my_customfield')
30 | class Meta:
31 | columns = ['customcolumn']
32 |
33 |
7 | {
8 | 'pk': 6, # ValuesDatatable requires automatic selection of 'pk'
9 | 'id': 6, # This happens to be the 'pk' field, so it found its way into the object
10 |
11 | # The full set of ORM names taken from the various ``column.sources`` lists.
12 | # Simple columns (where the names matched the orm query path) require no modification,
13 | # but notice the "blog__id" and "blog__name" entries that came from a single column
14 | # declaration.
15 | 'headline': u'',
16 | 'n_comments': 0,
17 | 'n_pingbacks': 0,
18 | 'pub_date': datetime.date(2013, 1, 1),
19 | 'blog__id': 2,
20 | 'blog__name': u'Second Blog',
21 |
22 | # Aliases have been given to the object for column names that didn't match the actual
23 | # ``sources`` names. Note how 'blog' is a list, because it had multiple sources.
24 | 'publication_date': datetime.date(2013, 1, 1),
25 | 'blog': [2, u'Second Blog'],
26 |
27 | }
28 |
6 | If a custom model field subclasses an existing Django field, that field will automatically
7 | be handled by your datatable's search function, as long as there are no custom query types
8 | that you want to support. However, if you have a field that does not subclass a built-in
9 | field, or does but supports additional query types, you must register that model field so
10 | that it can be correctly consulted for data prep and querying.
11 |
12 |
13 | For fields don't inherit from a Django field, don't offer special query types, and can
14 | generally be treated like a built-in field such as CharField or
15 | IntegerField, you can use a shorthand registration utility:
16 |
17 |
18 |
19 | # Somewhere in your project, such as models.py
20 | # This should happen globally so that it happens just once,
21 | # not every time a request occurs.
22 |
23 | from datatableview.columns import register_simple_modelfield
24 | register_simple_modelfield(MyCustomModelField)
25 |
26 |
27 |
Supporting custom ORM query types
28 |
29 | Custom fields with support for non-standard query types need to declare a special
30 | Column class to detail the relevant information. If the field supports a
31 | query lookup called '__customquery', you could declare the column like this:
32 |
33 |
34 | # This should happen somewhere in your project, such as columns.py,
35 | # but you should make sure that the file is imported somewhere,
36 | # such as in views.py.
37 |
38 | from datatableview.columns import Column
39 | class MyCustomColumn(Column):
40 | model_field_class = MyCustomFieldClass
41 | lookup_types = ['customquery']
42 |
43 |
44 | Columns declared like this are automatically registered with metaclass magic, placed at the
45 | top priority position of the datatableview.columns.COLUMN_CLASSES list, until
46 | another column is declared and takes the top-priority position. This allows you to override
47 | the built-in support for existing columns if you wish.
48 |
49 |
50 | Note that you should include ALL of the lookup types you want to support on a custom
51 | column, not just the new lookup types. Lookups should be chosen based on what you think
52 | would be intuitive. In text columns, icontains is chosen because partial
53 | matches are expected, and iexact isn't very useful because paired with
54 | icontains it doesn't find any additional results and yet by itself it's too
55 | inflexible.
56 |
50 | datatableview.finalizeOptions = (function(){
51 | var super_finalizeOptions = datatableview.finalizeOptions;
52 | return function _confirm_datatable_options(datatable, options){
53 | /**
54 | * Search our chechbox column by `data-idall` and add select plugin options to our checkboxes
55 | * Also we need to set class 'select-checkbox' for our
23 | Click on an example type in the sidebar to interact with a live table and read details on
24 | how it can be implemented.
25 |
26 |
27 |
Database status
28 | {% if db_works %}
29 |
30 | Your database tables appear migrated! Check out the example pages at your leisure.
31 |
32 | {% else %}
33 |
34 | Make sure you've done a migrate for the test project! This view failed to
35 | read from one of the example model tables!
36 |
37 |
38 | This project's settings use a simple sqlite3 file to keep things tidy for you, so we
39 | recommend using this project in its own environment. Ideally, you could run the
40 | manage.py script from inside the root of the django-datatable-view project
41 | directory:
42 |
52 | The demos and example code found in this example project all rely on the automatic
53 | initialization provided by a simple jQuery document.ready event. A couple of
54 | the pages use additional static resources (Bootstrap theme, x-editable); these pages include
55 | an HTML comment in the source that highlights their use in the live demo.
56 |
57 |
58 |
59 | Aside from the primary value of this set of examples as a reference site, noteworthy topics
60 | that branch outside of basic syntax are:
61 |
71 | The main model used in most of the examples is Entry, because of its diverse
72 | fields. Blog makes a simple relationship demonstration possible, and
73 | Author helps shed light on how to handle
74 | many to many relationships.
75 |
20 | This file introduces a global ``datatableview`` object that wraps the provided
21 | initialization function and internal and public utilities.
22 |
23 |
24 |
25 | To easily initialize datatables in Javascript, you can select your elements and send them
26 | to datatableview.initialize():
27 |
28 |
29 |
30 | $(function(){
31 | datatableview.initialize($('.mytable'));
32 |
33 | // Or, if there are common options that should be given to all select elements,
34 | // you can specify them now. data-* API attributes on the table columns will potentially
35 | // override the individual options.
36 | var common_options = {};
37 | datatableview.initialize($('.mytable'), common_options);
38 | });
39 |
40 |
41 |
Automatic no-configuration Initialization
42 |
43 | Before version 0.9, automatic initialzation was on by default. Although it now defaults to
44 | off, you can enable it by changing the setting on the global datatableview
45 | javascript variable:
46 |
47 |
48 |
49 | datatableview.auto_initialize = true;
50 |
51 |
52 |
53 | You can do this on a per-page basis, or make this part of your site's global javascript
54 | configuration. Make sure you set the flag before the jQuery(document).ready()
55 | handler is finished.
56 |
57 |
58 |
59 | Automatic initialization doesn't allow for easy per-table javascript configuration. To work
60 | around this quirk, you can use the now-deprecated global javascript
61 | function hook confirm_datatable_options(options, datatable), which is run
62 | against every table that initialized (the value of
63 | datatableview.auto_initialization doesn't matter!) in order to provide an
64 | opportunity to modify the options about to be sent to the underlying call to the real
65 | $(table).dataTable(options).
66 |
67 |
68 |
69 | Make sure you return the options object in the global hook!
70 |
71 |
72 |
73 | An example of how to declare this global hook follows:
74 |
75 |
76 |
77 | function confirm_datatable_options(options, datatable) {
78 | // "datatable" variable is the jQuery object, not the oTable, since it
79 | // hasn't been initialized yet.
80 | options.fnRowCallback = function(...){ ... };
81 | return options;
82 | }
83 |
84 |
85 |
86 | Note that this function hook is not required for the automatic initialization to
87 | work.
88 |
8 | A list of model fields that you wish to import into the table as columns, separate from any
9 | columns that are declared on the Datatable itself.
10 |
11 |
12 |
13 | In version 0.8 and earlier, an item in this list could name a related column or a model method
14 | and a column would be generated to handle the value. In 0.9 and later, this is no longer
15 | allowed. The Datatable configuration object in 0.9 needs an explicit name for
16 | every column, and specifying a related field such as "blog__name" starts mixing the
17 | concepts of column names and model fields. The columns list is for column names,
18 | not arbitrary ORM paths. To specify a related field, please see
19 | Related and virtual field columns.
20 |
21 |
22 |
23 | For legacy reasons, the Meta.columns setting can still be specified in the
24 | full-blown format that existed before version 0.9, but when 1.0 is released, this behavior will
25 | be removed.
26 |
8 | Setting footer to True will allow the default datatable rendering
9 | templates to show a simple <tfoot> in the table that shares the same labels
10 | that the header uses.
11 |
12 |
13 | On its own, this feature is available for added clarity when scrolling down a long table, but it
14 | can be used to set up more advanced features, such as adding column-specific search boxes for
15 | per-column searching (described by the official
16 | dataTables.js documentation
17 | here).
18 |
9 | A list of column names that should be hidden from the user on the frontend.
10 |
11 |
12 | The use case for this option is that a column might need to be generated and communicated during
13 | AJAX requests, but stripped out for that specific page. This might allow for reusable data
14 | sources where the page is modifying what is being seen. Because new data sources are easy to
15 | make, however, we recommend avoiding too much funny business.
16 |
17 |
18 | The "hidden" data is still going across the wire anyway, and is inspectable by prying eyes.
19 | Don't rely on this for sensitive data.
20 |
21 |
22 | Another use for this feature in dataTables.js is for plugins. Exporters like the CSV one have
23 | access to the "hidden" fields and can export them to a file even if they are not visible on the
24 | web page.
25 |
8 | The model that the table will represent. This helps the table automatically inspect fields
9 | named by the columns list and find their verbose_name settings. If
10 | this is not declared, it can be provided automatically by the model attribute on
11 | the view or even the incoming object_list (which is often a queryset).
12 |
9 | A list of field and/or column names describing how the table should be sorted by default. This
10 | overrides the model's own Meta.ordering setting, but will itself be overriden by
11 | the client if sorting operations are specified during AJAX requests.
12 |
13 |
14 | Like model ordering, field/column names can use a prefix '-' to indicate reverse
15 | ordering for that field.
16 |
8 | The page length sets the client option for how many records are shown in a single page of the
9 | datatable, which in turn controls how many objects are requested at a time from the server over
10 | AJAX.
11 |
12 |
13 | Make sure your table is javascript-configured to allow the page size you choose, since
14 | dataTables.js will allow you to return to your default setting if the user changes
15 | the page size away to something else. The demo on this page added a smaller option than is
16 | normally available by default in order to use a page size of 5 in the demo.
17 |
7 | Columns from demo:
8 |
9 | blog__name (not shown, but search "first" or "second" to find entries being filtered by this
10 | setting)
11 |
12 |
13 |
14 |
15 | Additional database fields to query for global searches can be specified with this setting.
16 |
17 |
18 | Normally you would simply back each of your columns with the correct list of database fields,
19 | but if valid searchable fields don't actually appear in the columns, you can have these extra
20 | fields appended to the list of searchable items without lying about the fields in your column
21 | specification.
22 |
8 | Defines a template path that the datatable should render on its initial non-AJAX page load.
9 | This is the template that appears on the main template when the
10 | {% templatetag openvariable %} datatable {% templatetag closevariable %} variable
11 | is rendered.
12 |
9 | A list of column names that should not be sortable by the client. This is enforced on the
10 | server side configuration validation as well, since a deliberate AJAX request to sort a column
11 | you wanted the client to disable could have very bad performance implications if it was allowed
12 | to have a go at the data.
13 |
7 | Before version 0.9 and the Datatable configuration class it introduced, all
8 | options had to be specified in one big dictionary and assigned as an attribute on the view.
9 | For simple tables, view attributes weren't that so bad, but it didn't take much to get
10 | out of control. You should avoid using it when building new tables.
11 |
12 |
13 |
14 | Using this configuration style, you name a model field and the table would find the model
15 | field's verbose name for display purposes. To use a custom verbose name, or if the column
16 | needed to show more than one model field worth of data (a method, property, two concatenated
17 | field values, etc) then the column had to become a 2- or 3-tuple of settings.
18 |
19 |
20 |
21 | The following formats are all valid (but deprecated) ways to write a column definition on
22 | a ``LegacyConfigurationDatatableView`` or ``LegacyDatatableView``, via the
23 | datatable_options class attribute dict:
24 |
25 |
26 | datatable_options = {
27 | 'columns': [
28 | # "concrete" field backed by the database
29 | 'name', # field's verbose_name will be used
30 | ("Name", 'name'), # custom verbose name provided
31 | ("Name", 'name', 'callback_attr_name'), # callback looked up on view
32 | ("Name", 'name', callback_handle), # calback used directly
33 |
34 | # non-field, but backed by methods, properties, etc
35 | "Virtual Field",
36 |
37 | # "fake" virtual field whose data is generated by the view
38 | ("Virtual Field", None, 'callback_attr_name'),
39 | ("Virtual Field", None, callback_handle),
40 | ],
41 | }
42 |
43 |
44 |
45 | For concrete fields that also provide callbacks, the actual database value will be consulted
46 | during searches and sorts, but the table will use the return value of the callback as the
47 | display data.
48 |
49 |
50 |
51 | Virtual fields are useful ways to mount methods onto a table. Consider a read-only property
52 | that generates its return value based on some underlying database field:
53 |
63 | Be careful with virtual columns that might cause database queries per-row. That doesn't
64 | scale very well!
65 |
66 | {% endblock content %}
67 |
--------------------------------------------------------------------------------
/demo_app/example_app/templatetags/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pivotal-energy-solutions/django-datatable-view/f142b3444e6377bb879f1c46f342e858ae9000eb/demo_app/example_app/templatetags/__init__.py
--------------------------------------------------------------------------------
/demo_app/example_app/templatetags/example_app_tags.py:
--------------------------------------------------------------------------------
1 | from django import template
2 | from django import get_version
3 | from django.urls import reverse
4 |
5 | register = template.Library()
6 |
7 | if get_version().split(".") < ["1", "5"]:
8 |
9 | @register.simple_tag(name="url")
10 | def django_1_4_url_simple(url_name):
11 | return reverse(url_name)
12 |
--------------------------------------------------------------------------------
/demo_app/example_app/urls.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | from django.urls import re_path
4 |
5 | from . import views
6 |
7 | urls = []
8 | for attr in dir(views):
9 | View = getattr(views, attr)
10 | try:
11 | is_demo = issubclass(View, views.DemoMixin) and View is not views.DemoMixin
12 | except TypeError:
13 | continue
14 | if is_demo:
15 | name = re.sub(r"([a-z]|[A-Z]+)(?=[A-Z])", r"\1-", attr).lower()
16 | name = name.replace("-datatable-view", "")
17 | urls.append(re_path(r"^{name}/$".format(name=name), View.as_view(), name=name))
18 |
19 | urlpatterns = [
20 | re_path(r"^$", views.IndexView.as_view(), name="index"),
21 | re_path(r"^reset/$", views.ResetView.as_view()),
22 | re_path(r"^migration-guide/$", views.MigrationGuideView.as_view(), name="migration-guide"),
23 | re_path(r"^column-formats/$", views.ValidColumnFormatsView.as_view(), name="column-formats"),
24 | re_path(
25 | r"^javascript-initialization/$",
26 | views.JavascriptInitializationView.as_view(),
27 | name="js-init",
28 | ),
29 | re_path(r"^satellite/$", views.SatelliteDatatableView.as_view(), name="satellite"),
30 | ] + urls
31 |
--------------------------------------------------------------------------------
/demo_app/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 |
4 | import os
5 | import sys
6 |
7 |
8 | def main():
9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "demo_app.settings")
10 | try:
11 | from django.core.management import execute_from_command_line
12 | except ImportError as exc:
13 | raise ImportError(
14 | "Couldn't import Django. Are you sure it's installed and "
15 | "available on your PYTHONPATH environment variable? Did you "
16 | "forget to activate a virtual environment?"
17 | ) from exc
18 | execute_from_command_line(sys.argv)
19 |
20 |
21 | if __name__ == "__main__":
22 | main()
23 |
--------------------------------------------------------------------------------
/demo_app/test_app/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pivotal-energy-solutions/django-datatable-view/f142b3444e6377bb879f1c46f342e858ae9000eb/demo_app/test_app/__init__.py
--------------------------------------------------------------------------------
/demo_app/test_app/fixtures/test_data.json:
--------------------------------------------------------------------------------
1 | [
2 | { "model": "test_app.RelatedModel", "pk": 1, "fields": {
3 | "name": "Related 1"
4 | }},
5 |
6 | { "model": "test_app.RelatedM2MModel", "pk": 1, "fields": {
7 | "name": "RelatedM2M 1"
8 | }},
9 |
10 | { "model": "test_app.ExampleModel", "pk": 1, "fields": {
11 | "name": "Example 1",
12 | "date_created": "2013-01-01T17:41:28+00:00"
13 | }},
14 | { "model": "test_app.ExampleModel", "pk": 2, "fields": {
15 | "name": "Example 2",
16 | "date_created": "2013-01-01T17:41:28+00:00",
17 | "related": 1
18 | }}
19 | ]
20 |
--------------------------------------------------------------------------------
/demo_app/test_app/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 |
4 | class ExampleModel(models.Model):
5 | name = models.CharField(max_length=64)
6 | value = models.BooleanField(default=False)
7 | date_created = models.DateTimeField(auto_now_add=True)
8 | related = models.ForeignKey("RelatedModel", blank=True, null=True, on_delete=models.CASCADE)
9 | relateds = models.ManyToManyField("RelatedM2MModel", blank=True)
10 |
11 | def __str__(self):
12 | return "ExampleModel %d" % (self.pk,)
13 |
14 | def __repr__(self):
15 | return "" % (
16 | self.pk,
17 | self.name,
18 | )
19 |
20 | def get_absolute_url(self):
21 | return "#{pk}".format(pk=self.pk)
22 |
23 | def get_negative_pk(self):
24 | return -1 * self.pk
25 |
26 |
27 | class RelatedModel(models.Model):
28 | name = models.CharField(max_length=64)
29 |
30 | def __str__(self):
31 | return "RelatedModel %d" % (self.pk,)
32 |
33 | def get_absolute_url(self):
34 | return "#{pk}".format(pk=self.pk)
35 |
36 |
37 | class RelatedM2MModel(models.Model):
38 | name = models.CharField(max_length=15)
39 |
40 |
41 | class ReverseRelatedModel(models.Model):
42 | name = models.CharField(max_length=15)
43 | example = models.ForeignKey("ExampleModel", on_delete=models.CASCADE)
44 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = _build
9 |
10 | # User-friendly check for sphinx-build
11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
13 | endif
14 |
15 | # Internal variables.
16 | PAPEROPT_a4 = -D latex_paper_size=a4
17 | PAPEROPT_letter = -D latex_paper_size=letter
18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
19 | # the i18n builder cannot share the environment and doctrees with the others
20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
21 |
22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext
23 |
24 | help:
25 | @echo "Please use \`make ' where is one of"
26 | @echo " html to make standalone HTML files"
27 | @echo " dirhtml to make HTML files named index.html in directories"
28 | @echo " singlehtml to make a single large HTML file"
29 | @echo " pickle to make pickle files"
30 | @echo " json to make JSON files"
31 | @echo " htmlhelp to make HTML files and a HTML help project"
32 | @echo " qthelp to make HTML files and a qthelp project"
33 | @echo " applehelp to make an Apple Help Book"
34 | @echo " devhelp to make HTML files and a Devhelp project"
35 | @echo " epub to make an epub"
36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
37 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
39 | @echo " text to make text files"
40 | @echo " man to make manual pages"
41 | @echo " texinfo to make Texinfo files"
42 | @echo " info to make Texinfo files and run them through makeinfo"
43 | @echo " gettext to make PO message catalogs"
44 | @echo " changes to make an overview of all changed/added/deprecated items"
45 | @echo " xml to make Docutils-native XML files"
46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes"
47 | @echo " linkcheck to check all external links for integrity"
48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
49 | @echo " coverage to run coverage check of the documentation (if enabled)"
50 |
51 | clean:
52 | rm -rf $(BUILDDIR)/*
53 |
54 | html:
55 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
56 | @echo
57 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
58 |
59 | dirhtml:
60 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
61 | @echo
62 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
63 |
64 | singlehtml:
65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
66 | @echo
67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
68 |
69 | pickle:
70 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
71 | @echo
72 | @echo "Build finished; now you can process the pickle files."
73 |
74 | json:
75 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
76 | @echo
77 | @echo "Build finished; now you can process the JSON files."
78 |
79 | htmlhelp:
80 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
81 | @echo
82 | @echo "Build finished; now you can run HTML Help Workshop with the" \
83 | ".hhp project file in $(BUILDDIR)/htmlhelp."
84 |
85 | qthelp:
86 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
87 | @echo
88 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
89 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
90 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-datatable-view.qhcp"
91 | @echo "To view the help file:"
92 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-datatable-view.qhc"
93 |
94 | applehelp:
95 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
96 | @echo
97 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
98 | @echo "N.B. You won't be able to view it unless you put it in" \
99 | "~/Library/Documentation/Help or install it in your application" \
100 | "bundle."
101 |
102 | devhelp:
103 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
104 | @echo
105 | @echo "Build finished."
106 | @echo "To view the help file:"
107 | @echo "# mkdir -p $$HOME/.local/share/devhelp/django-datatable-view"
108 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-datatable-view"
109 | @echo "# devhelp"
110 |
111 | epub:
112 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
113 | @echo
114 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
115 |
116 | latex:
117 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
118 | @echo
119 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
120 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
121 | "(use \`make latexpdf' here to do that automatically)."
122 |
123 | latexpdf:
124 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
125 | @echo "Running LaTeX files through pdflatex..."
126 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
127 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
128 |
129 | latexpdfja:
130 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
131 | @echo "Running LaTeX files through platex and dvipdfmx..."
132 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
133 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
134 |
135 | text:
136 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
137 | @echo
138 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
139 |
140 | man:
141 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
142 | @echo
143 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
144 |
145 | texinfo:
146 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
147 | @echo
148 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
149 | @echo "Run \`make' in that directory to run these through makeinfo" \
150 | "(use \`make info' here to do that automatically)."
151 |
152 | info:
153 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
154 | @echo "Running Texinfo files through makeinfo..."
155 | make -C $(BUILDDIR)/texinfo info
156 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
157 |
158 | gettext:
159 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
160 | @echo
161 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
162 |
163 | changes:
164 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
165 | @echo
166 | @echo "The overview file is in $(BUILDDIR)/changes."
167 |
168 | linkcheck:
169 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
170 | @echo
171 | @echo "Link check complete; look for any errors in the above output " \
172 | "or in $(BUILDDIR)/linkcheck/output.txt."
173 |
174 | doctest:
175 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
176 | @echo "Testing of doctests in the sources finished, look at the " \
177 | "results in $(BUILDDIR)/doctest/output.txt."
178 |
179 | coverage:
180 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
181 | @echo "Testing of coverage in the sources finished, look at the " \
182 | "results in $(BUILDDIR)/coverage/python.txt."
183 |
184 | xml:
185 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
186 | @echo
187 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
188 |
189 | pseudoxml:
190 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
191 | @echo
192 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
193 |
--------------------------------------------------------------------------------
/docs/datatableview/columns.rst:
--------------------------------------------------------------------------------
1 | ``columns``
2 | ===========
3 |
4 | .. py:module:: datatableview.columns
5 |
6 |
7 | Column
8 | ------
9 |
10 | .. autoclass:: Column
11 |
12 | Subclasses of :py:class:`Column` automatically register themselves as handlers of certain
13 | model fields, using :py:attr:`.model_field_class` and :py:attr:`.handles_field_classes` to offer
14 | support for whichever ``ModelField`` types they wish.
15 |
16 | External subclasses will automatically override those found in this module.
17 |
18 | Custom columns are not necessarily required when a third-party ``ModelField`` subclasses a
19 | built-in one, like ``CharField``. If the field however offers special query lookups, a dedicated
20 | column can be declared and the query lookups specified by its :py:attr:`lookup_types` list.
21 |
22 | :param str label: The verbose name shown on the table header. If omitted, the first item in
23 | ``sources`` is checked for a ``verbose_name`` of its own.
24 | :param list sources: A list of strings that define which fields on an object instance will be
25 | supply the value for this column. Model field names (including query
26 | language syntax) and model attribute, method, and property names are all
27 | valid source names. All sources in the list should share a common model
28 | field class. If they do not, see :py:class:`CompoundColumn` for information
29 | on separating the sources by type.
30 | :param str source: A convenience parameter for specifying just one source name. Cannot be used
31 | at the same time as ``sources``.
32 | :param processor: A reference to a callback that can modify the column source data before
33 | serialization and transmission to the client. Direct callable references will
34 | be used as-is, but strings will be used to look up that callable as a method of
35 | the column's :py:class:`~datatableview.datatables.Datatable` (or failing that,
36 | the view that is serving the table).
37 | :type processor: callable or str
38 | :param str separator: The string that joins multiple source values together if more than one
39 | source is declared. This is primarily a zero-configuration courtesy, and
40 | in most situations the developer should provide a :py:attr:`processor`
41 | callback that does the right thing with compound columns.
42 | :param str empty_value: The string shown as the column value when the column's source(s) are
43 | ``None``.
44 | :param bool sortable: Controls whether the rendered table will allow user sorting.
45 | :param bool visible: Controls whether the ``bVisible`` flag is set for the frontend. A
46 | ``visible=False`` column is still generated and transmitted over ajax
47 | requests for the table.
48 | :param bool localize: A special hint sent to processor callbacks to use.
49 | :param bool allow_regex: Adds ``__iregex`` as a query lookup type for this instance of the
50 | column.
51 | :param bool allow_full_text_search: Adds ``__search`` as a query lookup type for this instance of
52 | the column. Make sure your database backend and column type
53 | support this query type before enabling it.
54 |
55 | **Class Attributes**
56 |
57 | .. autoattribute:: model_field_class
58 |
59 | References the core ``ModelField`` type that can represent this column's data in the backend.
60 |
61 | .. autoattribute:: handles_field_classes
62 |
63 | References to additional ``ModelField`` types that can be supported by this column.
64 |
65 | .. autoattribute:: lookup_types
66 |
67 | ORM query types supported by the underlying :py:attr:`model_field_class`. The default types
68 | show bias for text-type fields in order for custom fields that don't subclass Django's
69 | ``CharField`` to be handled automatically.
70 |
71 | Subclasses should provide query lookups that make sense in the context of searching. If
72 | required, input coercion (from a string to some other type) should be handled in
73 | :py:meth:`.prep_search_value`, where you have the option to reject invalid search terms for a
74 | given lookup type.
75 |
76 | **Instance Attributes**
77 |
78 | .. attribute:: sources
79 |
80 | A list of strings that define which fields on an object instance will be supply the value for
81 | this column. Model field names and extra :py:class:`Datatable` column declarations are valid
82 | source names.
83 |
84 | .. attribute:: processor
85 |
86 | A reference to a callback that can modify the column source data before serialization and
87 | transmission to the client. Direct callable references will be used as-is, but strings will
88 | be used to look up that callable as a method of the column's :py:class:`Datatable` (or
89 | failing that, the view that is serving the table).
90 |
91 | **Properties**
92 |
93 | .. autoattribute:: attributes
94 |
95 | **Methods**
96 |
97 | .. automethod:: __str__
98 |
99 | Renders a simple ``
`` element with ``data-name`` attribute. All items found in the
100 | ``self.attributes`` dict are also added as attributes.
101 |
102 | .. automethod:: search
103 | .. automethod:: prep_search_value
104 | .. automethod:: value
105 | .. automethod:: get_initial_value
106 | .. automethod:: get_source_value
107 | .. automethod:: get_processor_kwargs
108 |
109 | **Internal Methods**
110 |
111 | .. automethod:: get_db_sources
112 | .. automethod:: get_virtual_sources
113 | .. automethod:: get_sort_fields
114 | .. automethod:: get_lookup_types
115 |
116 |
117 | Available Columns
118 | -----------------
119 |
120 | Model fields that subclass model fields shown here are automatically covered by these columns, which
121 | is why not all built-in model fields require their own column class, or are even listed in the
122 | handled classes.
123 |
124 |
125 | TextColumn
126 | ~~~~~~~~~~
127 |
128 | .. autoclass:: TextColumn(**kwargs)
129 |
130 | .. autoattribute:: model_field_class
131 | :annotation: = CharField
132 | .. autoattribute:: handles_field_classes
133 | :annotation: = [CharField, TextField, FileField]
134 | .. autoattribute:: lookup_types
135 |
136 | IntegerColumn
137 | ~~~~~~~~~~~~~
138 |
139 | .. autoclass:: IntegerColumn(**kwargs)
140 |
141 | .. autoattribute:: model_field_class
142 | :annotation: = IntegerField
143 | .. autoattribute:: handles_field_classes
144 | :annotation: = [IntegerField, AutoField]
145 | .. autoattribute:: lookup_types
146 |
147 | FloatColumn
148 | ~~~~~~~~~~~
149 |
150 | .. autoclass:: FloatColumn(**kwargs)
151 |
152 | .. autoattribute:: model_field_class
153 | :annotation: = FloatField
154 | .. autoattribute:: handles_field_classes
155 | :annotation: = [FloatField, DecimalField]
156 | .. autoattribute:: lookup_types
157 |
158 | DateColumn
159 | ~~~~~~~~~~
160 |
161 | .. autoclass:: DateColumn(**kwargs)
162 |
163 | .. autoattribute:: model_field_class
164 | :annotation: = DateField
165 | .. autoattribute:: handles_field_classes
166 | :annotation: = [DateField]
167 | .. autoattribute:: lookup_types
168 |
169 | DateTimeColumn
170 | ~~~~~~~~~~~~~~
171 |
172 | .. autoclass:: DateTimeColumn(**kwargs)
173 |
174 | .. autoattribute:: model_field_class
175 | :annotation: = DateTimeField
176 | .. autoattribute:: handles_field_classes
177 | :annotation: = [DateTimeField]
178 | .. autoattribute:: lookup_types
179 |
180 | BooleanColumn
181 | ~~~~~~~~~~~~~
182 |
183 | .. autoclass:: BooleanColumn(**kwargs)
184 |
185 | .. autoattribute:: model_field_class
186 | :annotation: = BooleanField
187 | .. autoattribute:: handles_field_classes
188 | :annotation: = [BooleanField, NullBooleanField]
189 | .. autoattribute:: lookup_types
190 |
191 | DisplayColumn
192 | ~~~~~~~~~~~~~
193 |
194 | .. autoclass:: DisplayColumn(**kwargs)
195 |
196 | .. autoattribute:: model_field_class
197 | :annotation: = None
198 | .. autoattribute:: handles_field_classes
199 | :annotation: = []
200 | .. autoattribute:: lookup_types
201 |
202 | CompoundColumn
203 | ~~~~~~~~~~~~~~
204 |
205 | .. autoclass:: CompoundColumn(**kwargs)
206 |
207 | .. autoattribute:: model_field_class
208 | :annotation: = None
209 | .. autoattribute:: handles_field_classes
210 | :annotation: = []
211 | .. autoattribute:: lookup_types
212 |
--------------------------------------------------------------------------------
/docs/datatableview/forms.rst:
--------------------------------------------------------------------------------
1 | ``forms``
2 | =========
3 | .. py:module:: datatableview.forms
4 |
5 |
6 | XEditableUpdateForm
7 | -------------------
8 |
9 | The X-Editable mechanism works by sending events to the view that indicate the user's desire to
10 | open a field for editing, and their intent to save a new value to the active record.
11 |
12 | The ajax ``request.POST['name']`` data field name that tells us which of the model
13 | fields should be targetted by this form. An appropriate formfield is looked up for that model
14 | field, and the ``request.POST['value']`` data will be inserted as the field's value.
15 |
16 | .. autoclass:: XEditableUpdateForm
17 |
18 | :param Model model: The model class represented in the datatable.
19 |
20 | .. automethod:: set_value_field
21 | .. automethod:: clean_name
22 |
--------------------------------------------------------------------------------
/docs/datatableview/helpers.rst:
--------------------------------------------------------------------------------
1 | ``helpers``
2 | ===========
3 | .. py:module:: datatableview.helpers
4 |
5 |
6 | The ``helpers`` module contains functions that can be supplied directly as a column's :py:attr:`~datatableview.columns.Column.processor`.
7 |
8 | Callbacks need to accept the object instance, and arbitrary other ``**kwargs``, because the ``Datatable`` instance will send it contextual information about the column being processed, such as the default value the column contains, the originating view, and any custom keyword arguments supplied by you from :py:meth:`~datatableview.datatables.Datatable.preload_record_data`.
9 |
10 | link_to_model
11 | -------------
12 |
13 | .. autofunction:: link_to_model(instance, text=None, **kwargs)
14 |
15 | make_boolean_checkmark
16 | ----------------------
17 |
18 | .. autofunction:: make_boolean_checkmark(value, true_value="✔", false_value="✘", *args, **kwargs)
19 |
20 | itemgetter
21 | ----------
22 |
23 | .. autofunction:: itemgetter
24 |
25 | attrgetter
26 | ----------
27 |
28 | .. autofunction:: attrgetter
29 |
30 | format_date
31 | -----------
32 |
33 | .. autofunction:: format_date
34 |
35 | format
36 | ------
37 |
38 | .. autofunction:: format(format_string, cast=)
39 |
40 | make_xeditable
41 | --------------
42 |
43 | .. autofunction:: make_xeditable
44 |
45 | make_processor
46 | --------------
47 |
48 | .. autofunction:: make_processor
49 |
--------------------------------------------------------------------------------
/docs/datatableview/index.rst:
--------------------------------------------------------------------------------
1 | ``datatableview`` module documentation
2 | ======================================
3 |
4 | .. toctree::
5 | views
6 | datatables
7 | columns
8 | forms
9 | helpers
10 |
--------------------------------------------------------------------------------
/docs/datatableview/views.rst:
--------------------------------------------------------------------------------
1 | ``views``
2 | =========
3 |
4 | DatatableView
5 | -------------
6 |
7 | .. py:module:: datatableview.views.base
8 |
9 |
10 | .. autoclass:: DatatableMixin
11 | :members:
12 |
13 | .. autoclass:: DatatableView
14 |
15 |
16 | ``views.xeditable``
17 | -------------------
18 | .. py:module:: datatableview.views.xeditable
19 |
20 |
21 | .. autoclass:: XEditableMixin
22 | :members:
23 |
24 | .. autoclass:: XEditableDatatableView
25 |
26 |
27 | ``views.legacy``
28 | ----------------
29 | .. py:module:: datatableview.views.legacy
30 |
31 |
32 | The ``legacy`` module holds most of the support utilities required to make the old tuple-based configuration syntax work.
33 |
34 | Use :py:class:`LegacyDatatableView` as your view's base class instead of :py:class:`DatatableView`, and then declare a class attribute ``datatable_options`` as usual. This strategy simply translates the old syntax to the new syntax. Certain legacy internal hooks and methods will no longer be available.
35 |
36 | .. autoclass:: LegacyDatatableMixin
37 |
38 | .. autoattribute:: datatable_options
39 | :annotation: = {}
40 |
41 | .. autoattribute:: datatable_class
42 | :annotation: = LegacyDatatable
43 |
44 | The :py:class:`~datatableview.datatables.LegacyDatatable` will help convert the more
45 | extravagant legacy tuple syntaxes into full :py:class:`~datatableview.columns.Column`
46 | instances.
47 |
48 | .. autoclass:: LegacyDatatableView
49 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. django-datatable-view documentation master file, created by
2 | sphinx-quickstart on Tue Oct 20 13:00:37 2015.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to django-datatable-view's documentation!
7 | =================================================
8 |
9 | For working demos and example code with explanations on common configurations, visit the demo site at http://example.com.
10 |
11 | Contents:
12 |
13 | .. toctree::
14 | :maxdepth: 4
15 |
16 | migration-guide
17 | topics/index
18 | datatableview/index
19 |
20 | Indices and tables
21 | ==================
22 |
23 | * :ref:`genindex`
24 | * :ref:`modindex`
25 | * :ref:`search`
26 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Command file for Sphinx documentation
4 |
5 | if "%SPHINXBUILD%" == "" (
6 | set SPHINXBUILD=sphinx-build
7 | )
8 | set BUILDDIR=_build
9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
10 | set I18NSPHINXOPTS=%SPHINXOPTS% .
11 | if NOT "%PAPER%" == "" (
12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
14 | )
15 |
16 | if "%1" == "" goto help
17 |
18 | if "%1" == "help" (
19 | :help
20 | echo.Please use `make ^` where ^ is one of
21 | echo. html to make standalone HTML files
22 | echo. dirhtml to make HTML files named index.html in directories
23 | echo. singlehtml to make a single large HTML file
24 | echo. pickle to make pickle files
25 | echo. json to make JSON files
26 | echo. htmlhelp to make HTML files and a HTML help project
27 | echo. qthelp to make HTML files and a qthelp project
28 | echo. devhelp to make HTML files and a Devhelp project
29 | echo. epub to make an epub
30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
31 | echo. text to make text files
32 | echo. man to make manual pages
33 | echo. texinfo to make Texinfo files
34 | echo. gettext to make PO message catalogs
35 | echo. changes to make an overview over all changed/added/deprecated items
36 | echo. xml to make Docutils-native XML files
37 | echo. pseudoxml to make pseudoxml-XML files for display purposes
38 | echo. linkcheck to check all external links for integrity
39 | echo. doctest to run all doctests embedded in the documentation if enabled
40 | echo. coverage to run coverage check of the documentation if enabled
41 | goto end
42 | )
43 |
44 | if "%1" == "clean" (
45 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
46 | del /q /s %BUILDDIR%\*
47 | goto end
48 | )
49 |
50 |
51 | REM Check if sphinx-build is available and fallback to Python version if any
52 | %SPHINXBUILD% 2> nul
53 | if errorlevel 9009 goto sphinx_python
54 | goto sphinx_ok
55 |
56 | :sphinx_python
57 |
58 | set SPHINXBUILD=python -m sphinx.__init__
59 | %SPHINXBUILD% 2> nul
60 | if errorlevel 9009 (
61 | echo.
62 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
63 | echo.installed, then set the SPHINXBUILD environment variable to point
64 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
65 | echo.may add the Sphinx directory to PATH.
66 | echo.
67 | echo.If you don't have Sphinx installed, grab it from
68 | echo.http://sphinx-doc.org/
69 | exit /b 1
70 | )
71 |
72 | :sphinx_ok
73 |
74 |
75 | if "%1" == "html" (
76 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
77 | if errorlevel 1 exit /b 1
78 | echo.
79 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
80 | goto end
81 | )
82 |
83 | if "%1" == "dirhtml" (
84 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
85 | if errorlevel 1 exit /b 1
86 | echo.
87 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
88 | goto end
89 | )
90 |
91 | if "%1" == "singlehtml" (
92 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
93 | if errorlevel 1 exit /b 1
94 | echo.
95 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
96 | goto end
97 | )
98 |
99 | if "%1" == "pickle" (
100 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
101 | if errorlevel 1 exit /b 1
102 | echo.
103 | echo.Build finished; now you can process the pickle files.
104 | goto end
105 | )
106 |
107 | if "%1" == "json" (
108 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
109 | if errorlevel 1 exit /b 1
110 | echo.
111 | echo.Build finished; now you can process the JSON files.
112 | goto end
113 | )
114 |
115 | if "%1" == "htmlhelp" (
116 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
117 | if errorlevel 1 exit /b 1
118 | echo.
119 | echo.Build finished; now you can run HTML Help Workshop with the ^
120 | .hhp project file in %BUILDDIR%/htmlhelp.
121 | goto end
122 | )
123 |
124 | if "%1" == "qthelp" (
125 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
126 | if errorlevel 1 exit /b 1
127 | echo.
128 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
129 | .qhcp project file in %BUILDDIR%/qthelp, like this:
130 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-datatable-view.qhcp
131 | echo.To view the help file:
132 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-datatable-view.ghc
133 | goto end
134 | )
135 |
136 | if "%1" == "devhelp" (
137 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
138 | if errorlevel 1 exit /b 1
139 | echo.
140 | echo.Build finished.
141 | goto end
142 | )
143 |
144 | if "%1" == "epub" (
145 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
146 | if errorlevel 1 exit /b 1
147 | echo.
148 | echo.Build finished. The epub file is in %BUILDDIR%/epub.
149 | goto end
150 | )
151 |
152 | if "%1" == "latex" (
153 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
154 | if errorlevel 1 exit /b 1
155 | echo.
156 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
157 | goto end
158 | )
159 |
160 | if "%1" == "latexpdf" (
161 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
162 | cd %BUILDDIR%/latex
163 | make all-pdf
164 | cd %~dp0
165 | echo.
166 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
167 | goto end
168 | )
169 |
170 | if "%1" == "latexpdfja" (
171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
172 | cd %BUILDDIR%/latex
173 | make all-pdf-ja
174 | cd %~dp0
175 | echo.
176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
177 | goto end
178 | )
179 |
180 | if "%1" == "text" (
181 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
182 | if errorlevel 1 exit /b 1
183 | echo.
184 | echo.Build finished. The text files are in %BUILDDIR%/text.
185 | goto end
186 | )
187 |
188 | if "%1" == "man" (
189 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
190 | if errorlevel 1 exit /b 1
191 | echo.
192 | echo.Build finished. The manual pages are in %BUILDDIR%/man.
193 | goto end
194 | )
195 |
196 | if "%1" == "texinfo" (
197 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
198 | if errorlevel 1 exit /b 1
199 | echo.
200 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
201 | goto end
202 | )
203 |
204 | if "%1" == "gettext" (
205 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
206 | if errorlevel 1 exit /b 1
207 | echo.
208 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
209 | goto end
210 | )
211 |
212 | if "%1" == "changes" (
213 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
214 | if errorlevel 1 exit /b 1
215 | echo.
216 | echo.The overview file is in %BUILDDIR%/changes.
217 | goto end
218 | )
219 |
220 | if "%1" == "linkcheck" (
221 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
222 | if errorlevel 1 exit /b 1
223 | echo.
224 | echo.Link check complete; look for any errors in the above output ^
225 | or in %BUILDDIR%/linkcheck/output.txt.
226 | goto end
227 | )
228 |
229 | if "%1" == "doctest" (
230 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
231 | if errorlevel 1 exit /b 1
232 | echo.
233 | echo.Testing of doctests in the sources finished, look at the ^
234 | results in %BUILDDIR%/doctest/output.txt.
235 | goto end
236 | )
237 |
238 | if "%1" == "coverage" (
239 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
240 | if errorlevel 1 exit /b 1
241 | echo.
242 | echo.Testing of coverage in the sources finished, look at the ^
243 | results in %BUILDDIR%/coverage/python.txt.
244 | goto end
245 | )
246 |
247 | if "%1" == "xml" (
248 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
249 | if errorlevel 1 exit /b 1
250 | echo.
251 | echo.Build finished. The XML files are in %BUILDDIR%/xml.
252 | goto end
253 | )
254 |
255 | if "%1" == "pseudoxml" (
256 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
257 | if errorlevel 1 exit /b 1
258 | echo.
259 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
260 | goto end
261 | )
262 |
263 | :end
264 |
--------------------------------------------------------------------------------
/docs/migration-guide.rst:
--------------------------------------------------------------------------------
1 | 0.9 Migration Guide
2 | ===================
3 |
4 | The jump from the 0.8.x series to 0.9 is covered in sections below.
5 |
6 | dataTables.js 1.10
7 | ------------------
8 |
9 | :Note: See `the official 1.10 announcement`__ if you've been living under a rock!
10 |
11 | __ http://datatables.net/blog/2014-05-01
12 |
13 | DataTables 1.10 provides a brand new api for getting things done, and it's a good thing too, because doing anything fancy in the old api pretty much required Allan to write yet another block of example code that everyone just copies and pastes.
14 |
15 | For our 0.9 release of django-datatable-view, we still use the "legacy" constructor to get things going, but that's okay, because the legacy api is still completely supported (even if all of its Hungarian notation keeps us up at night). The drawback at this stage is that we can't yet accept configuration settings that are "new-style only".
16 |
17 | Despite the fact that we're using the legacy constructor for a while longer, you can access the table's fancy new API object with one simple line::
18 |
19 | // Standard initialization
20 | var opts = {};
21 | var datatable = datatableview.initialize($('.datatable'), opts);
22 |
23 | // Get a reference to the new API object
24 | var table = datatable.api();
25 |
26 | Update configuration style
27 | --------------------------
28 |
29 | :Note: See `Datatable object and Meta`__ for examples.
30 |
31 | __ {% url "configure-datatable-object" %}
32 |
33 | The preferred way to configure columns for a view is now to use the :py:class:`~datatableview.datatables.Datatable` class. It has similarities to the Django ``ModelForm``: the class uses an inner :py:class:`~datatableview.datatables.Meta` class to specify all of the options that we used to provide in your view's ``datatable_options`` dict.
34 |
35 | You want to just unpack the keys and values from your existing ``datatable_options`` dict and set those as attributes on a :py:class:`~datatableview.datatables.Meta`. Then just assign this :py:class:`~datatableview.datatables.Datatable` subclass on your view::
36 |
37 | class MyDatatable(Datatable):
38 | class Meta:
39 | columns = [ ... ]
40 | search_fields = [ ... ]
41 | # etc
42 |
43 | class MyDatatableView(DatatableView):
44 | datatable_class = MyDatatable
45 |
46 | An alternate abbreviated style is available: as with class-based views that use Django forms, you can set these ``Meta`` attributes directly on the view class, `shown in more detail here`__. Please note that if you're declaring anything fancier than simple model fields or methods as columns (typically anything that would have required the 2-tuple or 3-tuple column syntax), please use the new ``Datatable`` object strategy.
47 |
48 | __ {% url "configure-inline" %}
49 |
50 | The new ``Datatable`` object doubles as the old 0.8 ``DatatableOptions`` template renderable object. ``DatatableOptions`` and ``utils.get_datatable_structure()`` have both been removed, since ``Datatable`` itself is all you need.
51 |
52 |
53 | New vocabulary
54 | --------------
55 |
56 | :Celebrate: We're becoming more sophisticated!
57 |
58 | Now that we spent a bunch of time learning how to use the tools we created, it felt like a good
59 | time to change some of the terms used internally.
60 |
61 | In connection with the new ``Datatable`` object that helps you design the datatable, **we've started referring to column data callbacks as "processors"**. This means that we will stop relying on callbacks in the documentation being named in the pattern of ``'get_column_FOO_data()'``. Instead, you'll notice names like ``'get_FOO_data()'``, and we'll be specifying the callback in a column definition via a ``processor`` keyword argument. See `Postprocessors`__ for a examples of this.
62 |
63 | __ {% url "processors" %}
64 |
65 |
66 | No more automatic column callbacks
67 | ----------------------------------
68 |
69 | :The Zen of Python: Explicit is better than implicit.
70 |
71 | We knew that implicit callbacks was a bad idea, but in our defense, `the deprecated column format was really cumbersome to use`__, and implicit callbacks were saving us some keystrokes. **This behavior is going away in version 1.0.** We continue to support implicit callbacks so that 0.9 is a backwards-compatible release with 0.8. If you have any column callbacks (we're calling them "processors" now) that aren't explicitly named in the column definition, please update your code soon!
72 |
73 | __ {% url "column-formats" %}
74 |
75 |
76 | No more automatic dataTables.js initialization
77 | ----------------------------------------------
78 |
79 | :Note: Bye bye ``function confirm_datatable_options(options){ ... }``
80 |
81 | Automatic initialization has gone the way of the buffalo, meaning that it doesn't exist anymore. The global JavaScript function ``confirm_datatable_options`` only ever existed because auto initialization took away your chance to set custom options during the init process. You should initialize your datatables via a simple call to the global function ``datatableview.initialize($('.datatable'), opts)``. This JS function reads DOM attributes from the table structure and builds some of the column options for you, but you can pass literally any other supported option in as the second argument. Just give it an object, and everything will be normal.
82 |
83 | There is a configurable Javascript flag ``datatableview.auto_initialize`` that
84 | previously defaulted to ``true``, but in 0.9 its default value is now
85 | ``false``. If you need 0.9 to behave the way it did in 0.8, set this flag globally
86 | or per-page as needed. (Be careful not to do it in a ``$(document).ready()``
87 | handler, since auto initialization runs during that hook. You might end up flagging for
88 | auto initialization after datatableview.js has already finished checking it, and nothing
89 | will happen.)
90 |
91 |
92 | Double check your default structure template
93 | --------------------------------------------
94 |
95 | :Note: See `Custom render template`__ for examples.
96 |
97 | __ {% url "customized-template" %}
98 |
99 | If you haven't gone out of your way to override the default structure template or create your own template, this shouldn't apply to you.
100 |
101 | The 0.9 default structure template at ``datatableview/default_structure.html`` has been modified to include a reference to a ``{% templatetag openvariable %} config {% templatetag closevariable %}`` variable, which holds all of the configuration values for the table. The render context for this template previously held a few select loose values for putting ``data-*`` attributes on the main ``
`` tag, but the template should now read from the following values (note the leading ``config.``:
102 |
103 | * ``{{ config.result_counter_id }}``
104 | * ``{{ config.page_length }}``
105 |
106 |
107 | Update complex column definitions
108 | ---------------------------------
109 |
110 | :Note: See `Custom verbose names`__, `Model method-backed columns`__, `Postprocessing values`__, and `Compound columns`__ for examples.
111 |
112 | __ /pretty-names/
113 | __ /column-backed-by-method/
114 | __ /processors/
115 | __ /compound-columns/
116 |
117 | The `now-deprecated 0.8 column definition format`__ had a lot of overloaded syntax. It grew out of a desire for a simple zero-configuration example, but became unwieldy, using nested tuples and optional tuple lengths to mean different things.
118 |
119 | __ /column-formats/
120 |
121 | The new format can be thought of as a clone of the built-in Django forms framework. In that comparison, the new ``Datatable`` class is like a Form, complete with Meta options that describe its features, and it defines ``Column`` objects instead of FormFields. A ``Datatable`` configuration object is then given to the view in the place of the old ``datatable_options`` dictionary.
122 |
123 | In summary, the old ``datatable_options`` dict is replaced by making a ``Datatable`` configuration object that has a ``Meta``.
124 |
125 | The task of `showing just a few specific columns`__ is made a bit heavier than before, but (as with the forms framework) the new Meta options can all be provided as class attributes on the view to keep the simplest cases simple.
126 |
127 | __ /specific-columns/
128 |
129 |
130 | Custom model fields
131 | -------------------
132 |
133 | :Note: See `Custom model fields`__ for new registration strategy.
134 |
135 | __ /custom-model-fields/
136 |
137 | Custom model fields were previously registered in a dict in ``datatableview.utils.FIELD_TYPES``, where the type (such as ``'text'``) would map to a list of model fields that conformed to the text-style ORM query types (such as ``__icontains``).
138 |
139 | In 0.9, the registration mechanism has changed to a priority system list, which associates instances of the new ``Column`` class to the model fields it can handle. See `Custom model fields`__ for examples showing how to register model fields to a built-in ``Column`` class, and how to write a new ``Column`` subclass if there are custom ORM query types that the field should support.
140 |
141 | __ /custom-model-fields/
142 |
143 |
144 | Experiment with the new ``ValuesDatatable``
145 | -------------------------------------------
146 |
147 | :Note: See `ValuesDatatable object`__ for examples.
148 |
149 | __ {% url "configure-values-datatable-object" %}
150 |
151 | An elegant simplification of the datatable strategy is to select the values you want to show directly from the database and just put them through to the frontend with little or no processing. If you can give up declaration of column sources as model methods and properties, and rely just on the data itself to be usable, try swapping in a ``ValuesDatatable`` as the base class for your table, rather than the default ``Datatable``.
152 |
153 | This saves Django the trouble of instantiating model instances for each row, and might even encourage the developer to think about their data with fewer layers of abstraction.
154 |
--------------------------------------------------------------------------------
/docs/topics/caching.rst:
--------------------------------------------------------------------------------
1 | Caching
2 | =======
3 |
4 | The caching system is opt-in on a per-:py:attr:`~datatableview.datatables.Datatable` basis.
5 |
6 | Each Datatable can specify in its :py:attr:`~datatableview.datatables.Meta` options a value for the :py:attr:`~datatableview.datatables.Meta.cache_type` option.
7 |
8 |
9 | Caching Strategies
10 | ------------------
11 |
12 | The possible values are available as constants on ``datatableview.datatables.cache_types``. Regardless of strategy, your `Settings`_ will control which Django-defined caching backend to use, and therefore the expiry time and other backend characteristics.
13 |
14 | ``cache_types.DEFAULT``
15 | ~~~~~~~~~~~~~~~~~~~~~~~
16 |
17 | A stand-in for whichever strategy ``DATATABLEVIEW_DEFAULT_CACHE_TYPE`` in your `Settings`_ specifies. That setting defaults to ``SIMPLE``.
18 |
19 | ``cache_types.SIMPLE``
20 | ~~~~~~~~~~~~~~~~~~~~~~
21 |
22 | Passes the ``object_list`` (usually a queryset) directly to the cache backend for pickling. This is a more faithful caching strategy than ``PK_LIST`` but becomes noticeably slower as the number of cached objects grows.
23 |
24 | ``cache_types.PK_LIST``
25 | ~~~~~~~~~~~~~~~~~~~~~~~
26 |
27 | Assumes that ``object_list`` is a queryset and stores in the cache only the list of ``pk`` values for each object. Reading from the cache therefore requires a database query to re-initialize the queryset, but because that query may be substantially faster than producing the original queryset, it is tolerated.
28 |
29 | Because this strategy must regenerate the queryset, extra information on the original queryset will be lost, such as calls to ``select_related()``, ``prefetch_related()``, and ``annotate()``.
30 |
31 | ``cache_types.NONE``
32 | ~~~~~~~~~~~~~~~~~~~~
33 |
34 | An explicit option that disables a caching strategy for a table. Useful when subclassing a Datatable to provide customized options.
35 |
36 |
37 | Settings
38 | --------
39 |
40 | There are a few project settings you can use to control features of the caching system when activated on a :py:attr:`~datatableview.datatables.Datatable`.
41 |
42 | ``DATATABLEVIEW_CACHE_BACKEND``
43 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
44 | :Default: ``'default'``
45 |
46 | The name of the Django ``CACHES`` backend to use. This is where cache expiry information will be
47 | specified.
48 |
49 | ``DATATABLEVIEW_CACHE_PREFIX``
50 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
51 | :Default: ``'datatableview_'``
52 |
53 | The prefix added to every cache key generated by a table's :py:meth:`~datatableview.datatables.Datatable.get_cache_key` value.
54 |
55 | ``DATATABLEVIEW_DEFAULT_CACHE_TYPE``
56 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
57 | :Default: ``'simple'`` (``datatableview.datatables.cache_types.SIMPLE``)
58 |
59 | The caching strategy to use when a Datatable's Meta option :py:attr:`~datatableview.datatables.Meta.cache_type` is set to ``cache_types.DEFAULT``.
60 |
61 | ``DATATABLEVIEW_CACHE_KEY_HASH``
62 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
63 | :Default: ``True``
64 |
65 | Controls whether the values that go into the cache key will be hashed or placed directly into the cache key string.
66 |
67 | This may be required for caching backends with requirements about cache key length.
68 |
69 | When ``False``, a cache key might resemble the following::
70 |
71 | datatableview_datatable_myproj.myapp.datatables.MyDatatable__view_myproj.myapp.views.MyView__user_77
72 |
73 | When ``True``, the cache key will be a predictable length, and might resemble the following::
74 |
75 | datatableview_datatable_3da541559918a808c2402bba5012f6c60b27661c__view_1161e6ffd3637b302a5cd74076283a7bd1fc20d3__user_77
76 |
77 |
78 | ``DATATABLEVIEW_CACHE_KEY_HASH_LENGTH``
79 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
80 | :Default: ``None``
81 |
82 | When `DATATABLEVIEW_CACHE_KEY_HASH`_ is ``True``, setting this to an integer will slice each hash substring to the first N characters, allowing you to further control the cache key length.
83 |
84 | For example, if set to ``10``, the hash-enabled cache key might resemble::
85 |
86 | datatableview_datatable_3da5415599__view_1161e6ffd3__user_77
87 |
--------------------------------------------------------------------------------
/docs/topics/custom-columns.rst:
--------------------------------------------------------------------------------
1 | Third-party model fields
2 | ========================
3 |
4 | Registering fields with custom columns
5 | --------------------------------------
6 |
7 | Any model field that subclasses a built-in Django field is automatically supported out of the box, as long as it supports the same query types (``__icontains``, ``__year``, etc) as the original field.
8 |
9 | A third-party field that is defined from scratch generally needs to become registered with a :py:class:`~datatableview.columns.Column`. The most straightforward thing to do is to subclass the base :py:class:`~datatableview.columns.Column`, and set the class attribute :py:attr:`~datatableview.columns.Column.model_field_class` to the third-party field. This will allow any uses of that model field to automatically select this new column as the handler for its values.
10 |
11 | Just by defining the column class, it will be registered as a valid candidate when model fields are automatically paired to column classes.
12 |
13 | **Important gotcha**: Make sure the custom class is imported somewhere in the project if you're not already explicitly using it on a table declaration. If the column is never imported, it won't be registered.
14 |
15 | If the column needs to indicate support for new query filter types, declare the class attribute :py:attr:`~datatableview.columns.Column.lookup_types` as a list of those operators (without any leading ``__``). You should only list query types that make sense when performing a search. For example, an ``IntegerField`` supports ``__lt``, but using that in searches would be unintuitive and confusing, so it is not included in the default implementation of :py:class:`~datatableview.columns.IntegerColumn`. You may find that ``exact`` is often the only sensible query type.
16 |
17 | New column subclasses are automatically inserted at the top of the priority list when the column system needs to discover a suitable column for a given model field. This is done to make sure that the system doesn't mistake a third-party field that subclasses a built-in one like ``CharField`` isn't actually mistaken for a simple ``CharField``.
18 |
19 | Skipping column registration
20 | ----------------------------
21 |
22 | Some column subclasses are not suitable for registration. For example, a custom column that is intended for use on only *some* ``CharField`` fields should definitely not attempt to register itself, since this would imply that all instances of ``CharField`` should use the new column. An example of this is the built-in :py:class:`~datatableview.columns.DisplayColumn`, which is a convenience class for representing a column that has no sources.
23 |
24 | By explicitly setting :py:attr:`~datatableview.columns.Column.model_field_class` to ``None``, the column will be unable to register itself as a handler for any specific model field. Consequently, it will be up to you to import and use the column where on tables where it makes sense.
25 |
--------------------------------------------------------------------------------
/docs/topics/index.rst:
--------------------------------------------------------------------------------
1 | Topics
2 | ======
3 |
4 | .. toctree::
5 | interaction-model
6 | searching
7 | sorting
8 | custom-columns
9 | caching
10 |
--------------------------------------------------------------------------------
/docs/topics/interaction-model.rst:
--------------------------------------------------------------------------------
1 | The client and server interaction model
2 | =======================================
3 |
4 | High-level description
5 | ----------------------
6 |
7 | Traditionally, developers using ``dataTables.js`` have approached their table designs from the client side. An ajax backend is just an implementation detail that can be enabled "if you need one."
8 |
9 | From the perspective of a Django application, however, we want to flip things around: the ``datatableview`` module has all of the tools required to build a server-side representation of your table, such as the column names, how it derives the information each column holds, and which sorting and filtering features it will expose.
10 |
11 | The execution steps for a server-driven table look like this:
12 |
13 | * The developer declares a view.
14 | * The view holds a table configuration object (like a Django ``ModelForm``).
15 | * The view puts the table object in the template rendering context.
16 | * The template renders the table object directly into the HTML, which includes its own template fragment to put the basic table structure on the page. (We happen to render a few ``data-*`` attributes on the ``
`` headers in the default template, but otherwise, the template isn't very interesting.)
17 | * The developer uses a javascript one-liner to initialize the table to get ``dataTables.js`` involved.
18 |
19 | From then on, the process is a loop of the user asking for changes to the table, and the server responding with the new data set:
20 |
21 | * The client sends an ajax request with ``GET`` parameters to the current page url.
22 | * The view uses the same table configuration object as before.
23 | * The view gives the table object the initial queryset.
24 | * The table configuration object overrides its default settings with any applicable ``GET`` parameters (sorting, searches, current page number, etc).
25 | * The table configuration object applies changes to the queryset.
26 | * The view serializes the final result set and responds to the client.
27 |
28 | Expanded details about some of these phases are found below.
29 |
30 | The table configuration object
31 | ------------------------------
32 |
33 | The :py:class:`~datatableview.datatables.Datatable` configuration object encapsulates everything that the server understands about the table. It knows how to render its initial skeleton as HTML, and it knows what to do with a queryset based on incoming ``GET`` parameter data from the client. It is designed to resemble the Django ``ModelForm``.
34 |
35 | The resemblance with ``ModelForm`` includes the use of an inner :py:class:`~datatableview.datatables.Meta` class, which can specify which model class the table is working with, which fields from that model to import, which column is sorted by default, which template is used to render the table's HTML skeleton, etc.
36 |
37 | :py:class:`~datatableview.columns.Column` s can be added to the table that aren't just simple model fields, however. Columns can declare any number of :py:attr:`~datatableview.columns.Column.sources`, including the output of instance methods and properties, all of which can then be formatted to a desired HTML result. Columns need not correspond to just a single model field!
38 |
39 | The column is responsible for revealing the data about an object (based on the ``sources`` it was given), and then formatting that data as a suitable final result (including HTML).
40 |
41 | Update the configuration from ``GET`` parameters
42 | ------------------------------------------------
43 |
44 | Many of the options declared on a :py:class:`~datatableview.datatables.Datatable` are considered protected. The column definitions themselves, for example, cannot be changed by a client playing with ``GET`` data. Similarly, the table knows which columns it holds, and it will not allow filters or sorting on data that it hasn't been instructed to inspect. ``GET`` parameters are normalized and ultimately thrown out if they don't agree with what the server-side table knows about the table.
45 |
46 | Generating the queryset filter
47 | ------------------------------
48 |
49 | Because each column in the table has its :py:attr:`~datatableview.columns.Column.sources` plainly declared by the developer, the table gathers all of the sources that represent model fields (even across relationships). For each such source, the table matches it to a core column type and uses that as an interface to ask for a ``Q()`` filter for a given search term.
50 |
51 | The table combines all of the discovered filters together, making a single ``Q()`` object, and then filters the queryset in a single step.
52 |
53 | Read :doc:`searching` for more information about how a column builds its ``Q()`` object.
54 |
55 | The client table HTML and javascript of course don't know anything about the server's notion of column sources, even when using column-specific filter widgets.
56 |
57 | Sorting the table by column
58 | ---------------------------
59 |
60 | Because a column is allowed to refer to more than one supporting data source, "sorting by a column" actually means that the list of sources is considered as a whole.
61 |
62 | Read :doc:`sorting` to understand the different ways sorting can be handled based on the composition of the column's sources.
63 |
64 | As with searching, the client table HTML and javascript have no visibility into the column's underlying sources. It simply asks for a certain column index to be sorted, and the server's table representation decides what that means.
65 |
--------------------------------------------------------------------------------
/docs/topics/searching.rst:
--------------------------------------------------------------------------------
1 | Searching
2 | =========
3 |
4 | All searching takes place on the server. Your view's :py:attr:`~datatableview.datatables.Datatable` is designed to have all the information it needs to respond to the ajax requests from the client, thanks to each column's :py:attr:`~datatableview.columns.Column.sources` list. The order in which the individual sources are listed does not matter (although it does matter for :doc:`sorting`).
5 |
6 | Sources that refer to non-``ModelField`` attributes (such as methods and properties of the object) are not included in searches. Manual searches would mean fetching the full, unfiltered queryset on every single ajax request, just to be sure that no results were excluded before a call to ``queryset.filter()``.
7 |
8 | Important terms concerning column :py:attr:`~datatableview.columns.Column.sources`:
9 |
10 | * **db sources**: Sources that are just fields managed by Django, supporting standard queryset lookups.
11 | * **Virtual sources**: Sources that reference not a model field, but an object instance method or property.
12 | * **Compound column**: A Column that declares more than one source.
13 | * **Pure db column, db-backed column**: A Column that defines only db-backed sources.
14 | * **Pure virtual column, virtual column**: A Column that defines only virtual sources.
15 | * **Sourceless column**: A Column that declares no sources at all (likely relying on its processor callback to compute some display value from the model instance).
16 |
17 | Parsing the search string
18 | -------------------------
19 |
20 | When given a search string, the :py:attr:`~datatableview.datatables.Datatable` splits up the string on spaces (except for quoted strings, which are protected). Each "term" is required to be satisfied somewhere in the object's collection of column :py:attr:`~datatableview.columns.Column.sources`.
21 |
22 | For each term, the table's :py:attr:`~datatableview.columns.Column` objects are asked to each provide a filter ``Q()`` object for that term.
23 |
24 | Deriving the ``Q()`` filter
25 | ---------------------------
26 |
27 | Terms are just free-form strings from the user, and may not be suitable for the column's data type. For example, the user could search for ``"54C-NN"``, and a integer-based column simply cannot coerce that term to something usable. Similar, searching for ``"13"`` is an integer, but isn't suitable for a ``DateTimeField`` to query as a ``__month``.
28 |
29 | Consequently, a column has the right to reject any search term that it is asked to build a query for. This allows columns to protect themselves from building invalid queries, and gives the developer a way to modify their own columns to decide what terms mean in the context of the data type they hold.
30 |
31 | A column's :py:meth:`~datatableview.columns.Column.search` method is called once per term. The default implementation narrows its :py:attr:`~datatableview.columns.Column.sources` down to just those that represent model fields, and then builds a query for each source, combining them with an ``OR`` operator. All of the different column ``Q()`` objects are then also combined with the ``OR`` operator, because global search terms can appear in any column.
32 |
33 | The only place an ``AND`` operator is used is from within the :py:attr:`~datatableview.datatables.Datatable`, which is combining all the results from the individual per-column term queries to make sure all terms are found.
34 |
35 | Compound columns with different data types
36 | ------------------------------------------
37 |
38 | Multiple sources in a single column don't need to be the same data type. This is a quirk of the column system. Each source is automatically matched to one of the provided :py:class:`~datatableview.columns.Column` classes, looked up based on the source's model field class. This allows the column to ask internal copies of those column classes for query information, respecting the differences between data types and coercion requirements.
39 |
--------------------------------------------------------------------------------
/docs/topics/sorting.rst:
--------------------------------------------------------------------------------
1 | Sorting
2 | =======
3 |
4 | All sorting takes place on the server. Your view's :py:attr:`~datatableview.datatables.Datatable` is designed to have all the information it needs to respond to the ajax requests from the client, thanks to each column's :py:attr:`~datatableview.columns.Column.sources` list. Unlike for searching, the order in which the individual sources are listed might matter to the user.
5 |
6 | Important terms concerning column :py:attr:`~datatableview.columns.Column.sources`:
7 |
8 | * **db sources**: Sources that are just fields managed by Django, supporting standard queryset lookups.
9 | * **Virtual sources**: Sources that reference not a model field, but an object instance method or property.
10 | * **Compound column**: A Column that declares more than one source.
11 | * **Pure db column, db-backed column**: A Column that defines only db-backed sources.
12 | * **Pure virtual column, virtual column**: A Column that defines only virtual sources.
13 | * **Sourceless column**: A Column that declares no sources at all (likely relying on its processor callback to compute some display value from the model instance).
14 |
15 | Pure database columns
16 | ---------------------
17 |
18 | The ideal scenario for speed and simplicity is that all :py:attr:`~datatableview.columns.Column.sources` are simply queryset lookup paths (to a local model field or to one that is related). When this is true, the sources list can be sent directly to ``queryset.order_by()``.
19 |
20 | Reversing the sort order will reverse all source components, converting a sources list such as ``['id', 'name']`` to ``['-id', '-name']``. This can be sent directly to ``queryset.order_by()`` as well.
21 |
22 | Mixed database and virtual sources
23 | ----------------------------------
24 |
25 | When a column has more than one source, the ``Datatable`` seeks to determine if there are ANY database sources at all. If there are, then the virtual ones are discarded for the purposes of sorting, and the strategy for pure database sorting can be followed.
26 |
27 | The strategic decision to keep or discard virtual sources is a complex one. We can't, in fact, just sort by the database fields first, and then blindly do a Python ``sort()`` on the resulting list, because the work performed by ``queryset.order_by()`` would be immediately lost. Any strategy that involves manually sorting on a virtual column must give up queryset ordering entirely, which makes the rationale for abandoning virtual sources easy to see.
28 |
29 | Pure virtual columns
30 | --------------------
31 |
32 | When a column provides only virtual sources, the whole queryset will in fact be evaluated as a list and the results sorted in Python accordingly.
33 |
34 | Please note that the performance penalty for this is undefined: the larger the queryset (after search filters have been applied), the harder the memory and speed penalty will be.
35 |
36 | Columns without sources
37 | -----------------------
38 |
39 | When no sources are available, the column automatically become unsortable by default. This is done to avoid allowing the column to claim the option to sort, yet do nothing when the user clicks on it.
40 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["hatchling", "hatch-vcs"]
3 | build-backend = "hatchling.build"
4 |
5 | [project]
6 | name = "django-datatable-view"
7 | dynamic = ["version"]
8 | description = "This package is used in conjunction with the jQuery plugin (http://http://datatables.net/), and supports state-saving detection with (http://datatables.net/plug-ins/api). The package consists of a class-based view, and a small collection of utilities for rendering table data from models."
9 | readme = "README.md"
10 | requires-python = ">=3.11"
11 | authors = [
12 | { name = "Pivotal Energy Solutions", email = "steve@pivotal.energy" },
13 | ]
14 | keywords = [
15 | "django",
16 | ]
17 | classifiers = [
18 | "Development Status :: 5 - Production/Stable",
19 | "Environment :: Web Environment",
20 | "Framework :: Django",
21 | "Framework :: Django :: 5.0",
22 | "Intended Audience :: Developers",
23 | "License :: OSI Approved :: Apache Software License",
24 | "Operating System :: OS Independent",
25 | "Programming Language :: Python",
26 | "Programming Language :: Python :: 3.11",
27 | "Programming Language :: Python :: 3.12",
28 | "Programming Language :: Python :: 3.13",
29 | "Topic :: Utilities",
30 | ]
31 | dependencies = [
32 | "django>=5.0",
33 | "python-dateutil",
34 | ]
35 |
36 | [project.optional-dependencies]
37 | test = [
38 | "django-environ",
39 | "mysqlclient",
40 | "coverage",
41 | "pre-commit",
42 | "black",
43 | "bandit",
44 | "ruff"
45 | ]
46 | docs = [
47 | "Sphinx",
48 | "sphinx-rtd-theme"
49 | ]
50 |
51 | [project.urls]
52 | Homepage = "https://github.com/pivotal-energy-solutions/django-datatable-view"
53 | Download = "https://github.com/pivotal-energy-solutions/django-datatable-view"
54 | Thanks = "https://saythanks.io/to/rh0dium"
55 | Source = "https://github.com/pivotal-energy-solutions/django-datatable-view/"
56 |
57 | [tool.hatch.version]
58 | source = "vcs"
59 |
60 | [tool.hatch.build.targets.sdist]
61 | include = [
62 | "/datatableview",
63 | "/datatableview/static/**/*",
64 | "/datatableview/templates/**/*",
65 | ]
66 |
67 | [tool.hatch.build.targets.wheel]
68 | packages = ['datatableview']
69 | include = [
70 | "/datatableview/static/**/*",
71 | "/datatableview/templates/**/*",
72 | ]
73 |
74 | [tool.black]
75 | line-length = 100
76 | target-version = ['py311']
77 | include = '\.pyi?$'
78 | exclude = '(\.git|.venv|_build|build|dist|.*\/__pycache__\/)'
79 |
80 | [tool.ruff]
81 | line-length = 100
82 | lint.ignore = ["F401"]
83 |
84 | [tool.bandit]
85 | targets = ['datatableview', "demo_app"]
86 | exclude_dirs = ["datatableview/tests"]
87 | skips = ["B303", "B308", "B323", "B324", "B703"]
88 |
89 | [tool.coverage.run]
90 | branch = true
91 | command_line = "demo_app/manage.py test --noinput --settings=demo_app.settings_test datatableview"
92 | omit = [
93 | "*/demo_app/**",
94 | "*/migrations/*",
95 | "*/tests/**",
96 | ]
97 |
98 | [tool.coverage.report]
99 | fail_under = 69
100 | precision = 1
101 | skip_covered = true
102 | skip_empty = true
103 | ignore_errors = true
104 | sort = "cover"
105 |
106 | [tool.bumper]
107 | exclude = [".idea", ".github", "demo_app"]
108 | version_files = ["datatableview/__init__.py"]
109 | repo = "pivotal-energy-solutions/django-datatable-view"
110 | report = "out.json"
111 |
--------------------------------------------------------------------------------