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 register() method:
import eav
eav.register(Parts)
or with decorators:
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:
- Attaches
EntityManagerto your class. By default, it replaces standard manager (objects). You can configure under which attribute it is accessible withEavConfig(see below). - Binds your model’s post_init signal with
attach_eav_attr()method. It is used to attachEntityhelper 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:
part.eav.weight = 0.56
part.save()
- Binds your model’s pre_save and post_save signals to
pre_save_handler()andpost_save_hander(), respectively. Those methods are responsible for validation and storage of attribute values. - Setups up generic relation to
Valueset. By default, it’s accessed under eav_values:
patient.eav_values.all()
# = <QuerySet [has fever?: "True" (1), temperature: 37.7 (2)]>
- Sets _eav_config_cls attribute storing model of the config class
used by
Registry. Defaults toEavConfig; 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: EavConfig passed to
register(). You can change them by overriding the class.
Available options are as follows:
manager_attr- Specifies manager name. Used to refer to the manager from Entity class, “objects” by default.manager_only- Specifies whether signals and generic relation should be setup for the registered model.eav_attr- Named of the Entity toolkit instance on the registered model instance. “eav” by default. See attach_eav_attr.generic_relation_attr- Name of the GenericRelation to Value objects. “eav_values” by default.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:
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, EavConfig provides classmethod
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:
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
IllegalAssignmentException. For example (extending
previous one):
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
Entity helper:
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 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:
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:
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:
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:
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:
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)