mirror of
https://github.com/jazzband/django-eav2.git
synced 2026-03-17 06:50:24 +00:00
314 lines
9.8 KiB
ReStructuredText
314 lines
9.8 KiB
ReStructuredText
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 appropriate place for the
|
|
statement would be at the bottom of your ``models.py`` or immediately 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``
|
|
*json* ``TYPE_JSON``
|
|
*csv* ``TYPE_CSV``
|
|
========= ==================
|
|
|
|
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)>
|
|
|
|
The attribute type *json* allows to store them in JSON format, which internally use JSONField:
|
|
|
|
.. code-block:: python
|
|
|
|
Attribute.objects.create(name='name_intl', datatype=Attribute.TYPE_JSON)
|
|
|
|
prod = Product.objects.create(sku='PRD00001', eav__name_intl={
|
|
"es": "Escoba Verde",
|
|
"en": "Green Broom",
|
|
"it": "Scopa Verde"
|
|
})
|
|
|
|
prod2 = Product.objects.create(sku='PRD00002', eav__name_intl={
|
|
"es": "Escoba Roja",
|
|
"en": "Red Broom"
|
|
})
|
|
|
|
prod3 = Product.objects.create(sku='PRD00003', eav__name_intl={
|
|
"es": "Escoba Azul",
|
|
"it": "Scopa Blu"
|
|
})
|
|
|
|
prod.eav.name_intl
|
|
{'es': 'Escoba Verde', 'en': 'Green Broom', 'it': 'Scopa Verde'}
|
|
|
|
type(prod.eav.name_intl)
|
|
dict
|
|
|
|
Product.objects.filter(eav__name_intl__has_key="it")
|
|
<EavQuerySet [<Product: PRD00001>, <Product: PRD00003>]>
|
|
|
|
The attribute type *csv* allows to store Comma Separated Values, using ";" as a separator:
|
|
|
|
.. code-block:: python
|
|
|
|
Attribute.objects.create(name='colors', datatype=Attribute.TYPE_CSV)
|
|
|
|
prod = Product.objects.create(sku='PRD00001', eav__colors="red;green;blue")
|
|
|
|
prod2 = Product.objects.create(sku='PRD00002', eav__colors="red;green")
|
|
|
|
prod3 = Product.objects.create(sku='PRD00003', eav__colors="red;blue")
|
|
|
|
prod4 = Product.objects.create(sku='PRD00004', eav__colors="")
|
|
|
|
prod.eav.colors
|
|
["red", "green", "blue"]
|
|
|
|
type(prod.eav.name_intl)
|
|
list
|
|
|
|
Product.objects.filter(eav__name_colors="green")
|
|
<EavQuerySet [<Product: PRD00001>, <Product: PRD00002>]>
|
|
|
|
Product.objects.filter(~Q(eav__name_colors__isnull=False))
|
|
<EavQuerySet [<Product: PRD00004>]>
|
|
|
|
|
|
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 works either by enum instance or by it's text representation as follows:
|
|
yes = EnumValue.objects.get(name='Yes')
|
|
Part.objects.filter(eav__is_available=yes) # via EnumValue
|
|
Part.objects.filter(eav__is_available='yes) # via EnumValue's value
|
|
|
|
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 seamlessly integrates with Django's admin interface by providing
|
|
dynamic attribute management directly within the admin panel. This feature
|
|
provides the EAV Attributes as a separate fieldset, whether use the base
|
|
fieldset or when providing your own.
|
|
|
|
.. 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)
|
|
|
|
Customizing the EAV Fieldset
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The Django EAV 2 integration allows you to customize the presentation of EAV
|
|
attributes in the admin interface through the use of a dedicated fieldset. You
|
|
can configure this fieldset by setting ``eav_fieldset_title`` and
|
|
``eav_fieldset_description`` within your admin class.
|