API Reference
If you are looking for information on a specific function, class, or method, this part of the documentation is for you.
Admin
This module contains classes used for admin integration.
- class BaseEntityAdmin(model, admin_site)[source]
- render_change_form(request, context, *args, **kwargs)[source]
Wrapper for
ModelAdmin.render_change_form. Replaces standard staticAdminFormwith an EAV-friendly one. The point is that our form generates fields dynamically and fieldsets must be inferred from a prepared and validated form instance, not just the form class. Django does not seem to provide hooks for this purpose, so we simply wrap the view and substitute some data.
- class BaseEntityInlineFormSet(data=None, files=None, instance=None, save_as_new=False, prefix=None, queryset=None, **kwargs)[source]
An inline formset that correctly initializes EAV forms.
- class BaseEntityInline(parent_model, admin_site)[source]
Inline model admin that works correctly with EAV attributes. You should mix in the standard
StackedInlineorTabularInlineclasses in order to define formset representation, e.g.:class ItemInline(BaseEntityInline, StackedInline): model = Item form = forms.ItemForm
Warning
TabularInlinedoes not work out of the box. There is, however, a patched templateadmin/edit_inline/tabular.htmlbundled with EAV-Django. You can copy or symlink theadmindirectory to your templates search path (see Django documentation).- formset
alias of
BaseEntityInlineFormSet
Decorators
This module contains pure wrapper functions used as decorators. Functions in this module should be simple and not involve complex logic.
Fields
- class EavDatatypeField(*args, db_collation=None, **kwargs)[source]
The datatype field used by
Attribute.
- class CSVField(separator=';', *args, **kwargs)[source]
- deconstruct()[source]
Return enough information to recreate the field as a 4-tuple:
The name of the field on the model, if contribute_to_class() has been run.
The import path of the field, including the class, e.g. django.db.models.IntegerField. This should be the most portable version, so less specific may be better.
A list of positional arguments.
A dict of keyword arguments.
Note that the positional or keyword arguments must contain values of the following types (including inner values of collection types):
None, bool, str, int, float, complex, set, frozenset, list, tuple, dict
UUID
datetime.datetime (naive), datetime.date
top-level classes, top-level functions - will be referenced by their full import path
Storage instances - these have their own deconstruct() method
This is because the values here must be serialized into a text format (possibly new Python code, possibly JSON) and these are the only types with encoding handlers defined.
There’s no need to return the exact way the field was instantiated this time, just ensure that the resulting field is the same - prefer keyword arguments over positional ones, and omit parameters with their default values.
Forms
This module contains forms used for admin integration.
- class BaseDynamicEntityForm(data=None, *args, **kwargs)[source]
ModelFormfor 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
date
SplitDateTimeField
bool
BooleanField
enum
ChoiceField
json
JSONField
csv
CSVField
- save(commit=True)[source]
Saves this
form’s cleaned_data into model instanceself.instanceand related EAV attributes. Returnsinstance.
- property media
Return all media required to render the widgets on this form.
Managers
This module contains the custom manager used by entities registered with eav.
Models
Along with the Entity helper class and EAVModelMeta
optional metaclass for each eav model class.
- class EnumValue(*args, **kwargs)[source]
EnumValue objects are the value ‘choices’ to multiple choice TYPE_ENUM
Attributeobjects. They have only one field, value, aCharFieldthat must be unique.For example:
yes = EnumValue.objects.create(value='Yes') # doctest: SKIP no = EnumValue.objects.create(value='No') unknown = EnumValue.objects.create(value='Unknown') ynu = EnumGroup.objects.create(name='Yes / No / Unknown') ynu.values.add(yes, no, unknown) Attribute.objects.create(name='has fever?', datatype=Attribute.TYPE_ENUM, enum_group=ynu) # = <Attribute: has fever? (Multiple Choice)>
Note
The same EnumValue objects should be reused within multiple EnumGroups. For example, if you have one EnumGroup called: Yes / No / Unknown and another called Yes / No / Not applicable, you should only have a total of four EnumValues objects, as you should have used the same Yes and No EnumValues for both EnumGroups.
- exception DoesNotExist
- exception MultipleObjectsReturned
- class EnumGroup(*args, **kwargs)[source]
EnumGroup objects have two fields - a name
CharFieldand values, aManyToManyFieldtoEnumValue.Attributeclasses with datatype TYPE_ENUM have aForeignKeyfield to EnumGroup.See
EnumValuefor an example.- exception DoesNotExist
- exception MultipleObjectsReturned
- class Attribute(*args, **kwargs)[source]
Putting the A in EAV. This holds the attributes, or concepts. Examples of possible Attributes: color, height, weight, number of children, number of patients, has fever?, etc…
Each attribute has a name, and a description, along with a slug that must be unique. If you don’t provide a slug, a default slug (derived from name), will be created.
The required field is a boolean that indicates whether this EAV attribute is required for entities to which it applies. It defaults to False.
Warning
Just like a normal model field that is required, you will not be able to save or create any entity object for which this attribute applies, without first setting this EAV attribute.
There are 7 possible values for datatype:
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)
Examples:
Attribute.objects.create(name='Height', datatype=Attribute.TYPE_INT) # = <Attribute: Height (Integer)> Attribute.objects.create(name='Color', datatype=Attribute.TYPE_TEXT) # = <Attribute: Color (Text)> yes = EnumValue.objects.create(value='yes') no = EnumValue.objects.create(value='no') unknown = EnumValue.objects.create(value='unknown') ynu = EnumGroup.objects.create(name='Yes / No / Unknown') ynu.values.add(yes, no, unknown) Attribute.objects.create(name='has fever?', datatype=Attribute.TYPE_ENUM, enum_group=ynu) # = <Attribute: has fever? (Multiple Choice)>
Warning
Once an Attribute has been used by an entity, you can not change it’s datatype.
- name
Main identifer for the attribute. Upon creation, slug is autogenerated from the name. (see
create_slug_from_name()).
- slug
Warning
This attribute should be used with caution. Setting this to True means that all entities that can have this attribute will be required to have a value for it.
- entity_ct
This field allows you to specify a relationship with any number of content types. This would be useful, for example, if you wanted an attribute to apply only to a subset of entities. In that case, you could filter by content type in the
get_attributes()method of that entity’s config.
- get_validators()[source]
Returns the appropriate validator function from
validatorsas a list (of length one) for the datatype.Note
The reason it returns it as a list, is eventually we may want this method to look elsewhere for additional attribute specific validators to return as well as the default, built-in one.
- validate_value(value)[source]
Check value against the validators returned by
get_validators()for this attribute.
- save(*args, **kwargs)[source]
Saves the Attribute and auto-generates a slug field if one wasn’t provided.
- clean()[source]
Validates the attribute. Will raise
ValidationErrorif the attribute’s datatype is TYPE_ENUM and enum_group is not set, or if the attribute is not TYPE_ENUM and the enum group is set.
- get_choices()[source]
Returns a query set of
EnumValueobjects for this attribute. Returns None if the datatype of this attribute is not TYPE_ENUM.
- save_value(entity, value)[source]
Called with entity, any Django object registered with eav, and value, the
Valuethis attribute for entity should be set to.If a
Valueobject for this entity and attribute doesn’t exist, one will be created.
- exception DoesNotExist
- exception MultipleObjectsReturned
- class Value(*args, **kwargs)[source]
Putting the V in EAV.
This model stores the value for one particular
Attributefor some entity.As with most EAV implementations, most of the columns of this model will be blank, as onle one value_ field will be used.
Example:
import eav from django.contrib.auth.models import User eav.register(User) u = User.objects.create(username='crazy_dev_user') a = Attribute.objects.create(name='Fav Drink', datatype='text') Value.objects.create(entity = u, attribute = a, value_text = 'red bull') # = <Value: crazy_dev_user - Fav Drink: "red bull">
- property value
Return the python object this value is holding.
- exception DoesNotExist
- exception MultipleObjectsReturned
- class Entity(instance)[source]
The helper class that will be attached to any entity registered with eav.
- static pre_save_handler(sender, *args, **kwargs)[source]
Pre save handler attached to self.instance. Called before the model instance we are attached to is saved. This allows us to call
validate_attributes()before the entity is saved.
- static post_save_handler(sender, *args, **kwargs)[source]
Post save handler attached to self.instance. Calls
save()when the model instance we are attached to is saved.
- get_all_attributes()[source]
Return a query set of all
Attributeobjects that can be set for this entity.
- validate_attributes()[source]
Called before
save(), first validate all the entity values to make sure they can be created / saved cleanly. RaisesValidationErrorif they can’t be.
Queryset
This module contains custom EavQuerySet class used for overriding
relational operators and pure functions for rewriting Q-expressions.
Q-expressions need to be rewritten for two reasons:
In order to hide implementation from the user and provide easy to use syntax sugar, i.e.:
Supplier.objects.filter(eav__city__startswith='New')
instead of:
city_values = Value.objects.filter(value__text__startswith='New') Supplier.objects.filter(eav_values__in=city_values)
For details see:
eav_filter().To ensure that Q-expression tree is compiled to valid SQL. For details see:
rewrite_q_expr().
- rewrite_q_expr(model_cls, expr)[source]
Rewrites Q-expression to safe form, in order to ensure that generated SQL is valid.
All EAV values are stored in a single table. Therefore, INNER JOIN generated for the AND-expression (1) will always fail, i.e. single row in a eav_values table cannot be both in two disjoint sets at the same time (and the whole point of using AND, usually, is two have two different sets). Therefore, we must paritially rewrite the expression so that the generated SQL is valid.
This is done by merging dangerous AND’s and substituting them with explicit
pk__infilter, where pks are taken from evaluted Q-expr branch.- Args:
- model_cls (TypeVar): 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
- Returns:
Union[Q, tuple]
- eav_filter(func)[source]
Decorator used to wrap filter and exclude methods. Passes args through
expand_q_filters()and kwargs throughexpand_eav_filter(). Returns the called function (filter or exclude).
- expand_q_filters(q, root_cls)[source]
Takes a Q object and a model class. Recursively passes each filter / value in the Q object tree leaf nodes through
expand_eav_filter().
- expand_eav_filter(model_cls, key, value)[source]
Accepts a model class and a key, value. Recurisively replaces any eav filter with a subquery.
For example:
key = 'eav__height' value = 5
Would return:
key = 'eav_values__in' value = Values.objects.filter(value_int=5, attribute__slug='height')
- class EavQuerySet(model=None, query=None, using=None, hints=None)[source]
Overrides relational operators for EAV models.
- filter(*args, **kwargs)[source]
Pass args and kwargs through
eav_filter(), then pass to theManagerfilter method.
- exclude(*args, **kwargs)[source]
Pass args and kwargs through
eav_filter(), then pass to theManagerexclude method.
- get(*args, **kwargs)[source]
Pass args and kwargs through
eav_filter(), then pass to theManagerget method.
Registry
This modules contains the registry classes.
- class EavConfig[source]
The default
EavConfigclass used if it is not overriden on registration. This is where all the default eav attribute names are defined.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.
- class Registry(model_cls)[source]
Handles registration through the
register()andunregister()methods.- static register(model_cls, config_cls=None)[source]
Registers model_cls with eav. You can pass an optional config_cls to override the EavConfig defaults.
Note
Multiple registrations for the same entity are harmlessly ignored.
Validators
This module contains a validator for each 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).
These validators are called by the
validate_value() method in the
Attribute model.
- validate_date(value)[source]
Raises
ValidationErrorunless value is an instance ofdatetimeordate
- validate_object(value)[source]
Raises
ValidationErrorunless value is a saved django model instance.
- validate_enum(value)[source]
Raises
ValidationErrorunless value is a savedEnumValuemodel instance.