mirror of
https://github.com/jazzband/django-eav2.git
synced 2026-04-24 17:04:43 +00:00
Rewrite documentation for modern Sphinx (#5)
This commit is contained in:
parent
b755c778d6
commit
d9dba5703c
28 changed files with 998 additions and 618 deletions
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
## Django EAV 2 - Entity-Attribute-Value storage for Django
|
||||
|
||||
Django EAV 2 is a fork of django-eav (which itself was derived from eav-django).
|
||||
You can find documentation <a href="https://django-eav-2.readthedocs.io/en/improvement-docs/">here</a>.
|
||||
Django EAV 2 is a fork of django-eav (which itself was derived from eav-django).
|
||||
You can find documentation <a href="https://django-eav-2.rtfd.io">here</a>.
|
||||
|
||||
## Installation
|
||||
You can install **django-eav2** from three sources:
|
||||
|
|
|
|||
|
|
@ -1,89 +1,20 @@
|
|||
# Makefile for Sphinx documentation
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
|
||||
SPHINXPROJ = DjangoEAV2
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
.PHONY: help Makefile
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-eav.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-eav.qhc"
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
|
||||
"run these through (pdf)latex."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
0
docs/_static/.gitignore
vendored
0
docs/_static/.gitignore
vendored
0
docs/_templates/.gitignore
vendored
0
docs/_templates/.gitignore
vendored
199
docs/conf.py
199
docs/conf.py
|
|
@ -1,199 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# django-eav documentation build configuration file, created by
|
||||
# sphinx-quickstart on Fri Sep 24 10:48:33 2010.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
sys.path.append(os.path.abspath(os.path.join('..','..')))
|
||||
|
||||
# django setup
|
||||
import settings
|
||||
from django.core.management import setup_environ
|
||||
setup_environ(settings)
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo','sphinxtogithub']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'django-eav'
|
||||
copyright = u'2010, MVP Africa'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.9'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.9.1'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of documents that shouldn't be included in the build.
|
||||
#unused_docs = []
|
||||
|
||||
# List of directories, relative to source directory, that shouldn't be searched
|
||||
# for source files.
|
||||
exclude_trees = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
html_theme = 'default'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_use_modindex = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = ''
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'django-eavdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
# The paper size ('letter' or 'a4').
|
||||
#latex_paper_size = 'letter'
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#latex_font_size = '10pt'
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'django-eav.tex', u'django-eav Documentation',
|
||||
u'MVP Africa', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#latex_preamble = ''
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_use_modindex = True
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
Docstrings
|
||||
==========
|
||||
|
||||
.. automodule:: eav
|
||||
:members:
|
||||
|
||||
.. automodule:: eav.models
|
||||
:members:
|
||||
|
||||
.. automodule:: eav.validators
|
||||
:members:
|
||||
|
||||
.. automodule:: eav.fields
|
||||
:members:
|
||||
|
||||
.. automodule:: eav.forms
|
||||
:members:
|
||||
|
||||
.. automodule:: eav.managers
|
||||
:members:
|
||||
|
||||
.. automodule:: eav.registry
|
||||
:members:
|
||||
|
||||
240
docs/index.rst
240
docs/index.rst
|
|
@ -1,240 +0,0 @@
|
|||
.. django-eav documentation master file, created by
|
||||
sphinx-quickstart on Fri Sep 24 10:48:33 2010.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
##########
|
||||
django-eav
|
||||
##########
|
||||
|
||||
|
||||
Introduction
|
||||
============
|
||||
django-eav provides an Entity-Attribute-Value storage model for django apps.
|
||||
|
||||
For a decent explanation of what an Entity-Attribute-Value storage model is,
|
||||
check `Wikipedia
|
||||
<http://en.wikipedia.org/wiki/Entity-attribute-value_model>`_.
|
||||
|
||||
.. note::
|
||||
This software was inspired / derived from the excellent `eav-django
|
||||
<http://pypi.python.org/pypi/eav-django/1.0.2>`_ written by Andrey
|
||||
Mikhaylenko.
|
||||
|
||||
There are a few notable differences between this implementation and the
|
||||
eav-django implementation.
|
||||
|
||||
* This one is called django-eav, whereas the other is called eav-django.
|
||||
* This app allows you to to 'attach' EAV attributes to any existing django
|
||||
model (even from third-party apps) without making any changes to the those
|
||||
models' code.
|
||||
* This app has slightly more robust (but still not perfect) filtering.
|
||||
|
||||
|
||||
Installation
|
||||
============
|
||||
You can install django-eav directly from guthub::
|
||||
|
||||
pip install -e git+git://github.com/mvpdev/django-eav.git#egg=django-eav
|
||||
|
||||
After installing, add ``eav`` to your ``INSTALLED_APPS`` in your
|
||||
project's ``settings.py`` file.
|
||||
|
||||
Usage
|
||||
=====
|
||||
In order to attach EAV attributes to a model, you first need to register it
|
||||
(just like you may register your models with django.contrib.admin).
|
||||
|
||||
Registration
|
||||
------------
|
||||
Registering a model with eav does a few things:
|
||||
|
||||
* Adds the eav :class:`eav.managers.EntityManager` to your class. By default,
|
||||
it will replace the default ``objects`` manager of the model, but you can
|
||||
choose to have the eav manager named something else if you don't want it to
|
||||
replace ``objects`` (see :ref:`advancedregistration`).
|
||||
* Connects the model's ``post_init`` signal to
|
||||
:meth:`~eav.registry.Registry.attach_eav_attr`. This function will attach
|
||||
the eav :class:`eav.models.Entity` helper class to every instance of your
|
||||
model when it is instatiated. By default, it will be attached to your models
|
||||
as an attribute named ``eav``, which will allow you to access it through
|
||||
``my_model_instance.eav``, but you can choose to name it something else if you
|
||||
want (again see :ref:`advancedregistration`).
|
||||
* Connect's the model's ``pre_save`` signal to
|
||||
:meth:`eav.models.Entity.pre_save_handler`.
|
||||
* Connect's the model's ``post_save`` signal to
|
||||
:meth:`eav.models.Entity.post_save_handler`.
|
||||
* Adds a generic relation helper to the class.
|
||||
* Sets an attribute called ``_eav_config_cls`` on the model class to either
|
||||
the default :class:`eav.registry.EavConfig` config class, or to the config
|
||||
class you provided during registration.
|
||||
|
||||
If that all sounds too complicated, don't worry, you really don't need to think
|
||||
about it. Just thought you should know.
|
||||
|
||||
Simple Registration
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
To register any model with EAV, you simply need to add the registration line
|
||||
somewhere that will be executed when django starts::
|
||||
|
||||
import eav
|
||||
eav.register(MyModel)
|
||||
|
||||
Generally, the most appropriate place for this would be in your ``models.py``
|
||||
immediately after your model definition.
|
||||
|
||||
.. _advancedregistration:
|
||||
|
||||
Advanced Registration
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
Advanced registration is only required if:
|
||||
|
||||
* You don't want eav to replace your model's default ``objects`` manager.
|
||||
* You want to name the :class:`~eav.models.Entity` helper attribute something
|
||||
other than ``eav``
|
||||
* You don't want all eav :class:`~eav.models.Attribute` objects to
|
||||
be able to be set for all of your registered models. In other words, you
|
||||
have different types of entities, each with different attributes.
|
||||
|
||||
Advanced registration is simple, and is performed the exact same way
|
||||
you override the django.contrib.admin registration defaults.
|
||||
|
||||
You just need to define your own config class that subclasses
|
||||
:class:`~eav.registry.EavConfig` and override the default class attributes
|
||||
and method.
|
||||
|
||||
There are five :class:`~eav.registry.EavConfig` class attributes you can
|
||||
override:
|
||||
|
||||
================================= ================================== ==========================================================================
|
||||
Class Attribute Default Description
|
||||
================================= ================================== ==========================================================================
|
||||
``manager_attr`` ``'objects'`` The name of the eav manager
|
||||
``manager_only`` ``False`` *boolean* Whether to *only* replace the manager, and do nothing else
|
||||
``eav_attr`` ``'eav'`` The attribute name of the entity helper
|
||||
``generic_relation_attr`` ``'eav_values'`` The attribute name of the generic relation helper
|
||||
``generic_relation_related_name`` The model's ``__class__.__name__`` The related name of the related name of the generic relation to your model
|
||||
================================= ================================== ==========================================================================
|
||||
|
||||
An example of just choosing a different name for the manager (and thus leaving
|
||||
``objects`` intact)::
|
||||
|
||||
class MyEavConfigClass(EavConfig):
|
||||
manager_attr = 'eav_objects'
|
||||
|
||||
eav.register(MyModel, MyEavConfigClass)
|
||||
|
||||
Additionally, :class:`~eav.registry.EavConfig` defines a classmethod called
|
||||
``get_attributes`` that, by default will return ``Attribute.objects.all()``
|
||||
This method is used to determine which :class:`~eav.models.Attribute` can be
|
||||
applied to the entity model you are registering. If you want to limit which
|
||||
attributes can be applied to your entity, you would need to override it.
|
||||
|
||||
For example::
|
||||
|
||||
class MyEavConfigClass(EavConfig):
|
||||
@classmethod
|
||||
def get_attributes(cls):
|
||||
return Attribute.objects.filter(type='person')
|
||||
|
||||
eav.register(MyModel, MyEavConfigClass)
|
||||
|
||||
|
||||
Using Attributes
|
||||
================
|
||||
Once you've registered your model(s), you can begin to use them with EAV
|
||||
attributes. Let's assume your model is called ``Person`` and it has one
|
||||
normal django ``CharField`` called name, but you want to be able to dynamically
|
||||
store other data about each Person.
|
||||
|
||||
First, let's create some attributes::
|
||||
|
||||
>>> Attribute.objects.create(name='Weight', datatype=Attribute.TYPE_FLOAT)
|
||||
>>> Attribute.objects.create(name='Height', datatype=Attribute.TYPE_INT)
|
||||
>>> Attribute.objects.create(name='Is pregant?', datatype=Attribute.TYPE_BOOLEAN)
|
||||
|
||||
Now let's create a patient, and set some of these attributes::
|
||||
|
||||
>>> p = Patient.objects.create(name='Bob')
|
||||
>>> p.eav.height = 46
|
||||
>>> p.eav.weight = 42.2
|
||||
>>> p.eav.is_pregnant = False
|
||||
>>> p.save()
|
||||
>>> bob = Patient.objects.get(name='Bob')
|
||||
>>> bob.eav.height
|
||||
46
|
||||
>>> bob.eav.weight
|
||||
42.2
|
||||
>>> bob.is_pregnant
|
||||
False
|
||||
|
||||
Additionally, assuming we're using the eav manager, we can also do::
|
||||
|
||||
>>> p = Patient.objects.create(name='Jen', eav__height=32, eav__pregnant=True)
|
||||
|
||||
|
||||
Filtering
|
||||
=========
|
||||
|
||||
eav attributes are filterable, using the same __ notation as django foreign
|
||||
keys::
|
||||
|
||||
Patient.objects.filter(eav__weight=42.2)
|
||||
Patient.objects.filter(eav__weight__gt=42)
|
||||
Patient.objects.filter(name='Bob', eav__weight__gt=42)
|
||||
Patient.objects.exclude(eav__is_pregnant=False)
|
||||
|
||||
You can even use Q objects, however there are some known issues
|
||||
(see :ref:`qobjectissue`) with Q object filters::
|
||||
|
||||
Patient.objects.filter(Q(name='Bob') | Q(eav__is_pregnant=False))
|
||||
|
||||
What about if you have a foreign key to a model that uses eav, but you want
|
||||
to filter from a model that doesn't use eav? For example, let's say you have
|
||||
a ``Patient`` model that **doesn't** use eav, but it has a foreign key to
|
||||
``Encounter`` that **does** use eav. You can even filter through eav across
|
||||
this relationship, but you need to use the eav manager for ``Patient``.
|
||||
|
||||
Just register ``Patient`` with eav, but set ``manager_only = True``
|
||||
see (see :ref:`advancedregistration`). Then you can do::
|
||||
|
||||
Patient.objects.filter(encounter__eav__weight=2)
|
||||
|
||||
|
||||
Admin Integration
|
||||
=================
|
||||
|
||||
You can even have your eav attributes show up just like normal fields in your
|
||||
models admin pages. Just register using the eav admin class::
|
||||
|
||||
from django.contrib import admin
|
||||
from eav.forms import BaseDynamicEntityForm
|
||||
from eav.admin import BaseEntityAdmin
|
||||
|
||||
class PatientAdminForm(BaseDynamicEntityForm):
|
||||
model = Patient
|
||||
|
||||
class PatientAdmin(BaseEntityAdmin):
|
||||
form = PatientAdminForm
|
||||
|
||||
admin.site.register(Patient, PatientAdmin)
|
||||
|
||||
|
||||
Known Issues
|
||||
============
|
||||
|
||||
.. _qobjectissue:
|
||||
|
||||
Q Object Filters
|
||||
----------------
|
||||
Due to an unexplained Q object / generic relation issue, exclude filters with
|
||||
EAV Q objects, or EAV Q objects ANDed together may produce inaccurate results.
|
||||
|
||||
Additional Resources
|
||||
====================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
docstrings
|
||||
|
||||
202
docs/source/_static/LICENSE.txt
Normal file
202
docs/source/_static/LICENSE.txt
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
BIN
docs/source/_static/RobotoSlab-Regular.ttf
Normal file
BIN
docs/source/_static/RobotoSlab-Regular.ttf
Normal file
Binary file not shown.
66
docs/source/_static/styles.css
Normal file
66
docs/source/_static/styles.css
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
@import url('https://fonts.googleapis.com/css?family=Roboto');
|
||||
|
||||
@font-face {
|
||||
font-family: "Roboto Slab";
|
||||
src: url("./RobotoSlab-Regular.ttf");
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: #f6f6f6 !important;
|
||||
}
|
||||
|
||||
.doc-title h1 {
|
||||
text-align: center;
|
||||
padding: 2rem !important;
|
||||
}
|
||||
|
||||
.doc-api {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3 {
|
||||
font-size: 21px !important;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
div.admonition p.admonition-title, div.sphinxsidebar h3, div.sphinxsidebar h4,
|
||||
div.sphinxsidebar input, div.body h1, div.body h2, div.body h3, div.body h4,
|
||||
div.body h5, div.body h6 { font-family: 'Roboto Slab', 'Helvetica', 'Arial',
|
||||
sans-serif; font-weight: 400; }
|
||||
div.body h1, div.body h2, div.body h3, div.body h4,
|
||||
div.body h5, div.body h6 { color: #353535; }
|
||||
pre, code { font-family: 'Ubuntu Mono', 'Consolas', 'Menlo',
|
||||
'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono';
|
||||
font-size: 15px; background: transparent; }
|
||||
pre, * pre { padding: 7px 0 7px 30px!important;
|
||||
margin: 15px 0!important;
|
||||
line-height: 1.3; }
|
||||
|
||||
|
||||
div.body { color: #3E4349; }
|
||||
a { color: #5D2CD1; }
|
||||
a:hover { color: #7546E3; }
|
||||
p.version-warning { background-color: #7546E3; }
|
||||
a.reference { border-bottom: 1px dotted #5D2CD1; }
|
||||
a.reference:hover { border-bottom: 1px solid #7546E3; }
|
||||
a.footnote-reference { border-bottom: 1px dotted #5D2CD1; }
|
||||
a.footnote-reference:hover { border-bottom: 1px solid #7546E3; }
|
||||
a:hover code { background-color: #eeeeee; }
|
||||
code.xref, a code { background-color: #E8EFF0;
|
||||
border-bottom: 1px solid white; }
|
||||
|
||||
div.indexwrapper h1 {
|
||||
text-indent: -999999px;
|
||||
background: url(click.png) no-repeat center center;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
|
||||
div.indexwrapper h1 {
|
||||
background: url(click@2x.png) no-repeat center center;
|
||||
background-size: 420px 175px;
|
||||
}
|
||||
}
|
||||
4
docs/source/_templates/layout.html
Normal file
4
docs/source/_templates/layout.html
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{% extends "!layout.html" %}
|
||||
{% block extrahead %}
|
||||
<a href="https://github.com/makimo/django-eav2"><img style="position: fixed; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a>
|
||||
{% endblock %}
|
||||
11
docs/source/_templates/sidebarintro.html
Normal file
11
docs/source/_templates/sidebarintro.html
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<h3>About</h3>
|
||||
<p>
|
||||
Django EAV 2 is an entity-attribute-value storage for modern Django.
|
||||
</p>
|
||||
<h3>Useful Links</h3>
|
||||
<ul>
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="https://pypi.org/project/django-eav2/">PyPI</a></li>
|
||||
<li><a href="http://github.com/makimo/django-eav2">GitHub</a></li>
|
||||
<li><a href="http://github.com/makimo/django-eav2/issues">Issue Tracker</a></li>
|
||||
</ul>
|
||||
3
docs/source/_templates/sidebarlogo.html
Normal file
3
docs/source/_templates/sidebarlogo.html
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<p class="logo"><a href="{{ pathto(master_doc) }}">
|
||||
<img class="logo" src="{{ pathto('_static/logo.png', 1) }}" width="250" height="50" alt="Logo">
|
||||
</a></p>
|
||||
74
docs/source/api.rst
Normal file
74
docs/source/api.rst
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
.. rst-class:: doc-api
|
||||
|
||||
API Reference
|
||||
=============
|
||||
|
||||
If you are looking for information on a specific function, class, or method,
|
||||
this part of the documentation is for you.
|
||||
|
||||
.. toctree::
|
||||
|
||||
|
||||
Admin
|
||||
-----
|
||||
|
||||
.. automodule:: eav.admin
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
Decorators
|
||||
----------
|
||||
|
||||
.. automodule:: eav.decorators
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
Fields
|
||||
------
|
||||
|
||||
.. automodule:: eav.fields
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
Forms
|
||||
-----
|
||||
|
||||
.. automodule:: eav.forms
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:exclude-members: FIELD_CLASSES
|
||||
|
||||
Managers
|
||||
--------
|
||||
|
||||
.. automodule:: eav.managers
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
Models
|
||||
------
|
||||
|
||||
.. automodule:: eav.models
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
Queryset
|
||||
--------
|
||||
|
||||
.. automodule:: eav.queryset
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
Registry
|
||||
--------
|
||||
|
||||
.. automodule:: eav.registry
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
Validators
|
||||
----------
|
||||
|
||||
.. automodule:: eav.validators
|
||||
:members:
|
||||
:member-order: bysource
|
||||
192
docs/source/conf.py
Normal file
192
docs/source/conf.py
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import django
|
||||
from django.conf import settings
|
||||
from sphinx.ext.autodoc import between
|
||||
|
||||
sys.path.insert(0, os.path.abspath('.'))
|
||||
sys.path.insert(0, os.path.abspath('../../'))
|
||||
|
||||
# Pass settings into configure.
|
||||
settings.configure(
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'eav'
|
||||
]
|
||||
)
|
||||
|
||||
# Call django.setup to load installed apps and other stuff.
|
||||
django.setup()
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'Django EAV 2'
|
||||
copyright = '2018, Iwo Herka and team at MAKIMO'
|
||||
author = '-'
|
||||
|
||||
# The short X.Y version
|
||||
version = ''
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = '0.10.0'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
extensions = [
|
||||
'sphinx.ext.napoleon',
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.viewcode',
|
||||
]
|
||||
|
||||
html_theme_options = dict(
|
||||
show_powered_by = False,
|
||||
show_related = True,
|
||||
fixed_sidebar = True,
|
||||
font_family = 'Roboto'
|
||||
)
|
||||
|
||||
templates_path = ['_templates']
|
||||
|
||||
source_suffix = '.rst'
|
||||
|
||||
master_doc = 'index'
|
||||
|
||||
language = None
|
||||
|
||||
exclude_patterns = []
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'alabaster'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Custom sidebar templates, must be a dictionary that maps document names
|
||||
# to template names.
|
||||
#
|
||||
# The default sidebars (for documents that don't match any pattern) are
|
||||
# defined by theme itself. Builtin themes are using these templates by
|
||||
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
|
||||
# 'searchbox.html']``.
|
||||
#
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_stylesheet('styles.css')
|
||||
app.connect('autodoc-process-docstring', between('^.*IGNORE.*$', exclude=True))
|
||||
return app
|
||||
|
||||
|
||||
html_sidebars = {
|
||||
'index': [
|
||||
'sidebarintro.html',
|
||||
'localtoc.html'
|
||||
],
|
||||
'**': [
|
||||
'sidebarintro.html',
|
||||
'localtoc.html',
|
||||
|
||||
'relations.html',
|
||||
'searchbox.html'
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ---------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'DjangoEAV2doc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'DjangoEAV2.tex', 'Django EAV 2 Documentation',
|
||||
'-', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'djangoeav2', 'Django EAV 2 Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output ----------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'DjangoEAV2', 'Django EAV 2 Documentation',
|
||||
author, 'DjangoEAV2', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# -- Extension configuration -------------------------------------------------
|
||||
|
||||
# -- Options for intersphinx extension ---------------------------------------
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {'https://docs.python.org/': None}
|
||||
|
||||
# -- Autodoc configuration ---------------------------------------------------
|
||||
|
||||
add_module_names = False
|
||||
45
docs/source/getting_started.rst
Normal file
45
docs/source/getting_started.rst
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
Getting Started
|
||||
===============
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
You can install **django-eav2** from PyPI, git or directly from source:
|
||||
|
||||
From PyPI
|
||||
^^^^^^^^^
|
||||
::
|
||||
|
||||
pip install django-eav2
|
||||
|
||||
With pip via git
|
||||
^^^^^^^^^^^^^^^^
|
||||
::
|
||||
|
||||
pip install git+https://github.com/makimo/django-eav2@master
|
||||
|
||||
From source
|
||||
^^^^^^^^^^^
|
||||
::
|
||||
|
||||
git clone git@github.com:makimo/django-eav2.git
|
||||
cd django-eav2
|
||||
python setup.py install
|
||||
|
||||
To uninstall::
|
||||
|
||||
python setup.py install --record files.txt
|
||||
rm $(cat files.txt)
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
After you've installed the package, you have to add it to your Django apps::
|
||||
|
||||
INSTALLED_APPS = [
|
||||
# ...
|
||||
|
||||
'eav',
|
||||
|
||||
# ...
|
||||
]
|
||||
72
docs/source/index.rst
Normal file
72
docs/source/index.rst
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
.. .. image:: _static/logo.png
|
||||
.. :align: center
|
||||
|
||||
.. rst-class:: doc-title
|
||||
|
||||
Django EAV 2
|
||||
============
|
||||
|
||||
Django EAV 2 is an entity-attribute-value storage for modern Django.
|
||||
Getting started is very easy.
|
||||
|
||||
**Step 1.** Register a model:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import eav
|
||||
eav.register(Supplier)
|
||||
|
||||
or with decorators:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from eav.decorators import register_eav
|
||||
|
||||
@register_eav
|
||||
class Supplier(models.Model):
|
||||
...
|
||||
|
||||
**Step 2.** Create an attribute:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
Attribute.objects.create(name='City', datatype=Attribute.TYPE_TEXT)
|
||||
|
||||
**Step 3.** That's it! You're ready to go:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
supplier.eav.city = 'London'
|
||||
supplier.save()
|
||||
|
||||
Supplier.objects.filter(eav__city='London')
|
||||
# = <EavQuerySet [<Supplier: Supplier object (1)>]>
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
getting_started
|
||||
usage
|
||||
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
If you are looking for information on a specific function, class, or
|
||||
method, this part of the documentation is for you.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
api
|
||||
|
||||
Indices and tables
|
||||
------------------
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
243
docs/source/usage.rst
Normal file
243
docs/source/usage.rst
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
Usage
|
||||
=====
|
||||
|
||||
This part of the documentation will take you through all of library's
|
||||
usage patterns. Before you can use EAV attributes, however, you need to
|
||||
register your models.
|
||||
|
||||
Simple Registration
|
||||
-------------------
|
||||
|
||||
Basic registration is very simple. You can do it with :func:`~eav.register` method:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import eav
|
||||
eav.register(Parts)
|
||||
|
||||
or with decorators:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from eav.decorators import register_eav
|
||||
|
||||
@register_eav
|
||||
class Supplier(models.Model):
|
||||
...
|
||||
|
||||
Generally, if you chose the former, the most appriopriate place for the
|
||||
statement would be at the bottom of your ``models.py`` or immmediately after
|
||||
model definition.
|
||||
|
||||
Advanced Registration
|
||||
---------------------
|
||||
|
||||
Under the hood, registration does a couple of things:
|
||||
|
||||
1. Attaches :class:`~eav.managers.EntityManager` to your class. By default,
|
||||
it replaces standard manager (*objects*). You can configure under which
|
||||
attribute it is accessible with :class:`~.eav.registry.EavConfig` (see below).
|
||||
|
||||
2. Binds your model's *post_init* signal with
|
||||
:meth:`~eav.registry.Registry.attach_eav_attr` method. It is used to
|
||||
attach :class:`~eav.models.Entity` helper object to each model instance.
|
||||
Entity, in turn, is used to retrieve, store and validate attribute values.
|
||||
By default, it's accessible under *eav* attribute:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
part.eav.weight = 0.56
|
||||
part.save()
|
||||
|
||||
3. Binds your model's *pre_save* and *post_save* signals to
|
||||
:meth:`~eav.models.Entity.pre_save_handler` and
|
||||
:meth:`~eav.models.Entity.post_save_hander`, respectively.
|
||||
Those methods are responsible for validation and storage
|
||||
of attribute values.
|
||||
|
||||
4. Setups up generic relation to :class:`~eav.models.Value` set.
|
||||
By default, it's accessed under *eav_values*:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
patient.eav_values.all()
|
||||
# = <QuerySet [has fever?: "True" (1), temperature: 37.7 (2)]>
|
||||
|
||||
5. Sets *_eav_config_cls* attribute storing model of the config class
|
||||
used by :class:`~eav.registry.Registry`. Defaults
|
||||
to :class:`~eav.registry.EavConfig`; can be overridden (see below).
|
||||
|
||||
With that out of the way, almost every aspect of the registration can
|
||||
be customized. All available options are provided to registration
|
||||
via config class: :class:`~eav.registry.EavConfig` passed to
|
||||
:meth:`~eav.register`. You can change them by overriding the class and passing
|
||||
it as a second argument. Available options are as follows:
|
||||
|
||||
1. ``manager_attr`` - Specifies manager name. Used to refer to the
|
||||
manager from Entity class, "objects" by default.
|
||||
2. ``manager_only`` - Specifies whether signals and generic relation should
|
||||
be setup for the registered model.
|
||||
3. ``eav_attr`` - Named of the Entity toolkit instance on the registered
|
||||
model instance. "eav" by default. See attach_eav_attr.
|
||||
4. ``generic_relation_attr`` - Name of the GenericRelation to Value
|
||||
objects. "eav_values" by default.
|
||||
5. ``generic_relation_related_name`` - Name of the related name for
|
||||
GenericRelation from Entity to Value. None by default. Therefore,
|
||||
if not overridden, it is not possible to query Values by Entities.
|
||||
|
||||
Example registration may look like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class SupplierEavConfig(EavConfig):
|
||||
manager_attr = 'eav_objects'
|
||||
|
||||
eav.register(supplier, SupplierEavConfig)
|
||||
|
||||
.. note::
|
||||
|
||||
As of now, configurable registration is not supported via
|
||||
class decorator. You have to use explicit method call.
|
||||
|
||||
Additionally, :class:`~eav.registry.EavConfig` provides *classmethod*
|
||||
:meth:`~eav.registry.EavConfig.get_attributes` which is used to determine
|
||||
a set of attributes available to a given model. By default, it returns
|
||||
``Attribute.objects.all()``. As usual, it can be customized:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from eav.models import Attribute
|
||||
|
||||
class SomeModelEavConfig(EavConfig):
|
||||
@classmethod
|
||||
def get_attributes(cls):
|
||||
return Attribute.objects.filter(slug__startswith='a')
|
||||
|
||||
Attribute validation includes checks against illegal attribute value
|
||||
assignments. This means that value assignments for attributes which are
|
||||
excluded for the model are treated with
|
||||
:class:`~eav.exceptions.IllegalAssignmentException`. For example (extending
|
||||
previous one):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
some_model.eav.beard = True
|
||||
some_model.save()
|
||||
|
||||
will throw an exception.
|
||||
|
||||
Creating Attributes
|
||||
-------------------
|
||||
|
||||
Once your models are registered, you can starting creating attributes for
|
||||
them. Two most important attributes of ``Attribute`` class are *slug* and
|
||||
*datatype*. *slug* is a unique global identifier (there must be at most
|
||||
one ``Attribute`` instance with given `slug`) and must be a valid Python
|
||||
variable name, as it's used to access values for that attribute from
|
||||
:class:`~eav.models.Entity` helper:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from eav.models import Attribute
|
||||
|
||||
Attribute.objects.create(slug='color', datatype=Attribute.TYPE_TEXT)
|
||||
flower.eav.color = 'red'
|
||||
|
||||
# Alternatively, assuming you're using default EntityManager:
|
||||
Attribute.objects.create(slug='color', datatype=Attribute.TYPE_TEXT)
|
||||
Flower.objects.create(name='rose', eav__color='red')
|
||||
|
||||
*datatype* determines type of attribute (and by extension type of value
|
||||
stored in :class:`~eav.models.Value`). Available choices are:
|
||||
|
||||
========= ==================
|
||||
Type Attribute Constant
|
||||
========= ==================
|
||||
*int* ``TYPE_INT``
|
||||
*float* ``TYPE_FLOAT``
|
||||
*text* ``TYPE_TEXT``
|
||||
*date* ``TYPE_DATE``
|
||||
*bool* ``TYPE_BOOLEAN``
|
||||
*object* ``TYPE_OBJECT``
|
||||
*enum* ``TYPE_ENUM``
|
||||
========= ==================
|
||||
|
||||
If you want to create an attribute with data-type *enum*, you need to provide
|
||||
it with ``enum_group``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from eav.models import EnumValue, EnumGroup, Attribute
|
||||
|
||||
true = EnumValue.objects.create(value='Yes')
|
||||
false = EnumValue.objects.create(value='No')
|
||||
bool_group = EnumGroup.objects.create(name='Yes / No')
|
||||
bool_group.enums.add(true, false)
|
||||
|
||||
Attribute.objects.create(
|
||||
name='hungry?',
|
||||
datatype=Attribute.TYPE_ENUM,
|
||||
enum_group=bool_group
|
||||
)
|
||||
# = <Attribute: hungry? (Multiple Choice)>
|
||||
|
||||
Finally, attribute type *object* allows to relate Django model instances
|
||||
via generic foreign keys:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
Attribute.objects.create(name='Supplier', datatype=Attribute.TYPE_OBJECT)
|
||||
|
||||
steve = Supplier.objects.create(name='Steve')
|
||||
cog = Part.objects.create(name='Cog', eav__supplier=steve)
|
||||
|
||||
cog.eav.supplier
|
||||
# = <Supplier: Steve (1)>
|
||||
|
||||
Filtering By Attributes
|
||||
-----------------------
|
||||
|
||||
Once you've created your attributes and values for them, you can use them
|
||||
to filter Django models. Django EAV 2 is using the same notation as Django's
|
||||
foreign-keys:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
Part.objects.filter(eav__weight=10)
|
||||
Part.objects.filter(eav__weight__gt=10)
|
||||
Part.objects.filter(eav__code__startswith='A')
|
||||
|
||||
# Of course, you can mix them with regular queries:
|
||||
Part.objects.filter(name='Cog', eav__height=7.8)
|
||||
|
||||
# Querying enums looks as follows:
|
||||
yes = EnumValue.objects.get(name='Yes')
|
||||
Part.objects.filter(eav__is_available=yes)
|
||||
|
||||
You can use ``Q`` expressions too:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
Patient.objects.filter(
|
||||
Q(eav__sex='male', eav__fever=no) | Q(eav__city='Nice') & Q(eav__age__gt=32)
|
||||
)
|
||||
|
||||
Admin Integration
|
||||
-----------------
|
||||
|
||||
Django EAV 2 includes integration for Django's admin. As usual, you need to
|
||||
register your model first:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.contrib import admin
|
||||
from eav.forms import BaseDynamicEntityForm
|
||||
from eav.admin import BaseEntityAdmin
|
||||
|
||||
class PatientAdminForm(BaseDynamicEntityForm):
|
||||
model = Patient
|
||||
|
||||
class PatientAdmin(BaseEntityAdmin):
|
||||
form = PatientAdminForm
|
||||
|
||||
admin.site.register(Patient, PatientAdmin)
|
||||
17
eav/admin.py
17
eav/admin.py
|
|
@ -1,8 +1,7 @@
|
|||
'''Admin. This module contains classes used for admin integration.'''
|
||||
'''This module contains classes used for admin integration.'''
|
||||
|
||||
from django.contrib import admin
|
||||
from django.contrib.admin.options import InlineModelAdmin
|
||||
from django.contrib.admin.options import ModelAdmin
|
||||
from django.contrib.admin.options import InlineModelAdmin, ModelAdmin
|
||||
from django.forms.models import BaseInlineFormSet
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
|
|
@ -34,18 +33,19 @@ class BaseEntityAdmin(ModelAdmin):
|
|||
|
||||
|
||||
class BaseEntityInlineFormSet(BaseInlineFormSet):
|
||||
"""
|
||||
'''
|
||||
An inline formset that correctly initializes EAV forms.
|
||||
"""
|
||||
'''
|
||||
def add_fields(self, form, index):
|
||||
if self.instance:
|
||||
setattr(form.instance, self.fk.name, self.instance)
|
||||
form._build_dynamic_fields()
|
||||
|
||||
super(BaseEntityInlineFormSet, self).add_fields(form, index)
|
||||
|
||||
|
||||
class BaseEntityInline(InlineModelAdmin):
|
||||
"""
|
||||
'''
|
||||
Inline model admin that works correctly with EAV attributes. You should mix
|
||||
in the standard StackedInline or TabularInline classes in order to define
|
||||
formset representation, e.g.::
|
||||
|
|
@ -54,12 +54,11 @@ class BaseEntityInline(InlineModelAdmin):
|
|||
model = Item
|
||||
form = forms.ItemForm
|
||||
|
||||
.. warning: TabularInline does *not* work out of the box. There is,
|
||||
.. warning:: TabularInline does *not* work out of the box. There is,
|
||||
however, a patched template `admin/edit_inline/tabular.html` bundled
|
||||
with EAV-Django. You can copy or symlink the `admin` directory to your
|
||||
templates search path (see Django documentation).
|
||||
|
||||
"""
|
||||
'''
|
||||
formset = BaseEntityInlineFormSet
|
||||
|
||||
def get_fieldsets(self, request, obj=None):
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
'''
|
||||
Decorators.
|
||||
|
||||
This module contains pure wrapper functions used as decorators.
|
||||
Functions in this module should be simple and not involve complex logic.
|
||||
'''
|
||||
|
||||
def register_eav(**kwargs):
|
||||
'''
|
||||
Registers the given model(s) classes and wrapped Model class with
|
||||
django-eav::
|
||||
Registers the given model(s) classes and wrapped ``Model`` class with
|
||||
Django EAV 2::
|
||||
|
||||
@register_eav
|
||||
class Author(models.Model):
|
||||
|
|
|
|||
|
|
@ -1,11 +1,3 @@
|
|||
'''
|
||||
Fields.
|
||||
|
||||
Contains two custom fields:
|
||||
* :class:`EavSlugField`
|
||||
* :class:`EavDatatypeField`
|
||||
'''
|
||||
|
||||
import re
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
|
|
@ -23,7 +15,7 @@ class EavSlugField(models.SlugField):
|
|||
Slugs are used to convert the Python attribute name to a database
|
||||
lookup and vice versa. We need it to be a valid Python identifier.
|
||||
We don't want it to start with a '_', underscore will be used
|
||||
var variables we don't want to be saved in db.
|
||||
var variables we don't want to be saved in the database.
|
||||
'''
|
||||
super(EavSlugField, self).validate(value, instance)
|
||||
slug_regex = r'[a-z][a-z0-9_]*'
|
||||
|
|
|
|||
18
eav/forms.py
18
eav/forms.py
|
|
@ -1,4 +1,4 @@
|
|||
'''Forms. This module contains forms used for admin integration.'''
|
||||
'''This module contains forms used for admin integration.'''
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
|
|
@ -10,13 +10,25 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
class BaseDynamicEntityForm(ModelForm):
|
||||
'''
|
||||
ModelForm for entity with support for EAV attributes. Form fields are
|
||||
created on the fly depending on Schema defined for given entity instance.
|
||||
``ModelForm`` for entity with support for EAV attributes. Form fields are
|
||||
created on the fly depending on schema defined for given entity instance.
|
||||
If no schema is defined (i.e. the entity instance has not been saved yet),
|
||||
only static fields are used. However, on form validation the schema will be
|
||||
retrieved and EAV fields dynamically added to the form, so when the
|
||||
validation is actually done, all EAV fields are present in it (unless
|
||||
Rubric is not defined).
|
||||
|
||||
Mapping between attribute types and field classes is as follows:
|
||||
|
||||
===== =============
|
||||
Type Field
|
||||
===== =============
|
||||
text CharField
|
||||
float IntegerField
|
||||
int DateTimeField
|
||||
bool BooleanField
|
||||
enum ChoiceField
|
||||
===== =============
|
||||
'''
|
||||
FIELD_CLASSES = {
|
||||
'text': CharField,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'''
|
||||
Managers.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
This module contains the custom manager used by entities registered with eav.
|
||||
'''
|
||||
from django.db import models
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
'''
|
||||
Models.
|
||||
|
||||
This module defines the four concrete, non-abstract models:
|
||||
* :class:`Value`
|
||||
* :class:`Attribute`
|
||||
|
|
|
|||
|
|
@ -1,26 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Queryset.
|
||||
|
||||
This module contains custom EavQuerySet class used for overriding
|
||||
This module contains custom :class:`EavQuerySet` class used for overriding
|
||||
relational operators and pure functions for rewriting Q-expressions.
|
||||
Q-expressions need to be rewritten for two reasons:
|
||||
|
||||
1. In order to hide implementation from the user and provide easy to use
|
||||
syntax sugar, i.e.::
|
||||
1. In order to hide implementation from the user and provide easy to use
|
||||
syntax sugar, i.e.::
|
||||
|
||||
Supplier.objects.filter(eav__city__startswith='New')
|
||||
Supplier.objects.filter(eav__city__startswith='New')
|
||||
|
||||
instead of::
|
||||
instead of::
|
||||
|
||||
city_values = Value.objects.filter(value__text__startswith='New')
|
||||
Supplier.objects.filter(eav_values__in=city_values)
|
||||
city_values = Value.objects.filter(value__text__startswith='New')
|
||||
Supplier.objects.filter(eav_values__in=city_values)
|
||||
|
||||
For details see: ``eav_filter``.
|
||||
For details see: :func:`eav_filter`.
|
||||
|
||||
2. To ensure that Q-expression tree is compiled to valid SQL.
|
||||
For details see: ``rewrite_q_expr``.
|
||||
2. To ensure that Q-expression tree is compiled to valid SQL.
|
||||
For details see: :func:`rewrite_q_expr`.
|
||||
'''
|
||||
|
||||
from functools import wraps
|
||||
|
|
@ -37,7 +35,7 @@ def is_eav_and_leaf(expr, gr_name):
|
|||
Checks whether Q-expression is an EAV AND leaf.
|
||||
|
||||
Args:
|
||||
expr (Q | tuple): Q-expression to be checked.
|
||||
expr (Union[Q, tuple]): Q-expression to be checked.
|
||||
gr_name (str): Generic relation attribute name, by default 'eav_values'
|
||||
|
||||
Returns:
|
||||
|
|
@ -53,6 +51,7 @@ def rewrite_q_expr(model_cls, expr):
|
|||
Rewrites Q-expression to safe form, in order to ensure that
|
||||
generated SQL is valid.
|
||||
|
||||
IGNORE:
|
||||
Suppose we have the following Q-expression:
|
||||
|
||||
└── OR
|
||||
|
|
@ -63,6 +62,7 @@ def rewrite_q_expr(model_cls, expr):
|
|||
│ └── eav_values__in [4, 5]
|
||||
└── AND
|
||||
└── eav_values__in [6, 7, 8]
|
||||
IGNORE
|
||||
|
||||
All EAV values are stored in a single table. Therefore, INNER JOIN
|
||||
generated for the AND-expression (1) will always fail, i.e.
|
||||
|
|
@ -71,23 +71,25 @@ def rewrite_q_expr(model_cls, expr):
|
|||
two different sets). Therefore, we must paritially rewrite the
|
||||
expression so that the generated SQL is valid::
|
||||
|
||||
IGNORE:
|
||||
└── OR
|
||||
├── AND
|
||||
│ └── eav_values__in [1, 2, 3]
|
||||
└── AND
|
||||
└── pk__in [1, 2]
|
||||
IGNORE
|
||||
|
||||
This is done by merging dangerous AND's and substituting them with
|
||||
explicit ``pk__in`` filter, where pks are taken from evaluted
|
||||
Q-expr branch.
|
||||
|
||||
Args:
|
||||
model_cls (Model class): model class used to construct QuerySet
|
||||
from leaf attribute-value expression.
|
||||
expr: (Q | tuple): Q-expression (or attr-val leaf) to be rewritten.
|
||||
model_cls (TypeVar): model class used to construct :meth:`QuerySet`
|
||||
from leaf attribute-value expression.
|
||||
expr: (Q | tuple): Q-expression (or attr-val leaf) to be rewritten.
|
||||
|
||||
Returns:
|
||||
Q | tuple
|
||||
Union[Q, tuple]
|
||||
'''
|
||||
# Node in a Q-expr can be a Q or an attribute-value tuple (leaf).
|
||||
# We are only interested in Qs.
|
||||
|
|
@ -153,7 +155,7 @@ def rewrite_q_expr(model_cls, expr):
|
|||
def eav_filter(func):
|
||||
'''
|
||||
Decorator used to wrap filter and exclude methods. Passes args through
|
||||
expand_q_filters and kwargs through expand_eav_filter. Returns the
|
||||
:func:`expand_q_filters` and kwargs through :func:`expand_eav_filter`. Returns the
|
||||
called function (filter or exclude).
|
||||
'''
|
||||
@wraps(func)
|
||||
|
|
@ -188,7 +190,7 @@ def expand_q_filters(q, root_cls):
|
|||
'''
|
||||
Takes a Q object and a model class.
|
||||
Recursively passes each filter / value in the Q object tree leaf nodes
|
||||
through expand_eav_filter
|
||||
through :func:`expand_eav_filter`.
|
||||
'''
|
||||
new_children = []
|
||||
|
||||
|
|
@ -259,23 +261,23 @@ class EavQuerySet(QuerySet):
|
|||
@eav_filter
|
||||
def filter(self, *args, **kwargs):
|
||||
'''
|
||||
Pass *args* and *kwargs* through ``eav_filter``, then pass to
|
||||
the ``models.Manager`` filter method.
|
||||
Pass *args* and *kwargs* through :func:`eav_filter`, then pass to
|
||||
the ``Manager`` filter method.
|
||||
'''
|
||||
return super(EavQuerySet, self).filter(*args, **kwargs)
|
||||
|
||||
@eav_filter
|
||||
def exclude(self, *args, **kwargs):
|
||||
'''
|
||||
Pass *args* and *kwargs* through ``eav_filter``, then pass to
|
||||
the ``models.Manager`` exclude method.
|
||||
Pass *args* and *kwargs* through :func:`eav_filter`, then pass to
|
||||
the ``Manager`` exclude method.
|
||||
'''
|
||||
return super(EavQuerySet, self).exclude(*args, **kwargs)
|
||||
|
||||
@eav_filter
|
||||
def get(self, *args, **kwargs):
|
||||
'''
|
||||
Pass *args* and *kwargs* through ``eav_filter``, then pass to
|
||||
the ``models.Manager`` get method.
|
||||
Pass *args* and *kwargs* through :func:`eav_filter`, then pass to
|
||||
the ``Manager`` get method.
|
||||
'''
|
||||
return super(EavQuerySet, self).get(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
'''Registry. This modules contains the registry classes.'''
|
||||
'''This modules contains the registry classes.'''
|
||||
|
||||
from django.contrib.contenttypes import fields as generic
|
||||
from django.db.models.signals import post_init, post_save, pre_save
|
||||
|
|
@ -9,21 +9,22 @@ from .models import Attribute, Entity, Value
|
|||
|
||||
class EavConfig(object):
|
||||
'''
|
||||
The default EavConfig class used if it is not overriden on registration.
|
||||
The default ``EavConfig`` class used if it is not overriden on registration.
|
||||
This is where all the default eav attribute names are defined.
|
||||
|
||||
Available options are as follows:
|
||||
1. manager_attr - Specifies manager name. Used to refer to the
|
||||
manager from Entity class, "objects" by default.
|
||||
2. manager_only - Specifies whether signals and generic relation should
|
||||
be setup for the registered model.
|
||||
3. eav_attr - Named of the Entity toolkit instance on the registered
|
||||
model instance. "eav" by default. See attach_eav_attr.
|
||||
4. generic_relation_attr - Name of the GenericRelation to Value
|
||||
objects. "eav_values" by default.
|
||||
5. generic_relation_related_name - Name of the related name for
|
||||
GenericRelation from Entity to Value. None by default. Therefore,
|
||||
if not overridden, it is not possible to query Values by Entities.
|
||||
|
||||
1. manager_attr - Specifies manager name. Used to refer to the
|
||||
manager from Entity class, "objects" by default.
|
||||
2. manager_only - Specifies whether signals and generic relation should
|
||||
be setup for the registered model.
|
||||
3. eav_attr - Named of the Entity toolkit instance on the registered
|
||||
model instance. "eav" by default. See attach_eav_attr.
|
||||
4. generic_relation_attr - Name of the GenericRelation to Value
|
||||
objects. "eav_values" by default.
|
||||
5. generic_relation_related_name - Name of the related name for
|
||||
GenericRelation from Entity to Value. None by default. Therefore,
|
||||
if not overridden, it is not possible to query Values by Entities.
|
||||
'''
|
||||
manager_attr = 'objects'
|
||||
manager_only = False
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
'''Utilities. This module contains non-essential helper methods.'''
|
||||
'''This module contains non-essential helper methods.'''
|
||||
|
||||
import sys
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Validtors.
|
||||
|
||||
This module contains a validator for each Attribute datatype.
|
||||
This module contains a validator for each :class:`~eav.models.Attribute` datatype.
|
||||
|
||||
A validator is a callable that takes a value and raises a ``ValidationError``
|
||||
if it doesn’t meet some criteria. (see
|
||||
`django validators <http://docs.djangoproject.com/en/dev/ref/validators/>`_)
|
||||
if it doesn’t meet some criteria (see `Django validators
|
||||
<http://docs.djangoproject.com/en/dev/ref/validators/>`_).
|
||||
|
||||
These validators are called by the
|
||||
:meth:`~eav.models.Attribute.validate_value` method in the
|
||||
|
|
|
|||
Loading…
Reference in a new issue