django-eav2/docs/index.rst
2010-09-29 13:04:02 +03:00

237 lines
8.8 KiB
ReStructuredText

.. 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://http://en.wikipedia.org/wiki/Entity-attribute-value_model>`_.
.. note::
This software was inspired / derrived 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 implimentation 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
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 accross
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 innacurate results.
Additional Resources
====================
.. toctree::
:maxdepth: 2
docstrings