[eav-django] Object related Schema

179 views
Skip to first unread message

llonchj

unread,
May 9, 2010, 4:07:42 AM5/9/10
to eav-django
Hello,

This is my first post to this list, I'm developing a django based open
source application for renewable energy management.

I'm looking for a EAV schema implementation in django and I want to
know if with eav-django it's possible to set a schema for an specific
group of objects, for example:

Having an "item" model which defines a product (brand,model) and a
"component" model that identifies each unique product with (serial
number, *item_id*). I want to define different attributes for every
component based on the defined item schema.

It's possible to implement this in EAV?

Thanks,

Andy

unread,
May 9, 2010, 5:16:54 AM5/9/10
to eav-django
Hello,

This is almost exactly the use case for which EAV-Django was
developed. I will try to provide a complete example later but for now
will give a hint (in pseudo-Djangoish Python):

Component.schemata = ManyToManyField(Schema)
Item.component = FK(Component)
Item.get_schemata_for_model = classmethod(lambda cls:
Schema.objects.all())
Item.get_schemata_for_instance = lambda self, qs:
qs.filter(components=self.component)

Such approach should be enough to enable the schema you have
described. Hope this helps.

Cheers,
Andy

Andy

unread,
May 9, 2010, 5:18:07 AM5/9/10
to eav-django
...on the second thought, it seems that I've misread your use case.
Sorry, will re-read a bit later.

Andy

Andy

unread,
May 9, 2010, 10:35:19 AM5/9/10
to eav-django
I'd like to properly reformulate my response to the use case as I
(mis)understood it at first. I thought you wanted items and
categories, where categories define structure of items. To achieve
this you would define models this way:


class Schema(BaseSchema):
pass

class Category(Model):
schemata = ManyToManyField(Schema, related_name='categories')

class Item(BaseEntity):
brand = CharField(...)
model = CharField(...)
category = ForeignKey(Category)

@classmethod
def get_schemata_for_model(cls):
return Schema.objects.all()

def get_schemata_for_instance(self, qs):
"""Returns schemata filtered by this item's component."""
return qs.filter(categories=self.category)


Then I understood that you wanted something different, but I can't
figure out what exactly. Could you please provide some examples?

llonchj

unread,
May 9, 2010, 9:00:27 PM5/9/10
to eav-django
Hi Andy,

Here it is a partial implementatione example, Watch at the Schema
class definition and Item.get_schemata_for_model.

Once implemented, object has to be saved in django admin in order to
show the schema.

The get_schemata_for_model is a key on the product.

Thank you

--------------------------------------------------------------
# django
from django.db import models

from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic

from django.utils.translation import ugettext_lazy as _

# eav
from eav.models import BaseChoice, BaseEntity, BaseSchema,
BaseAttribute

class Family(models.Model):
title = models.CharField(max_length=50)

def __unicode__(self):
return self.title

class Item(BaseEntity):
family = models.ForeignKey(Family, blank=True, null=True)
title = models.CharField(max_length=50)

### @classmethod
def get_schemata_for_model(self):
s = Schema.objects
if self.family:
return s.filter(family__id__exact = self.family.id)
else:
return s.all()

def __unicode__(self):
return self.title


class Schema(BaseSchema):
family = models.ForeignKey(Family, blank=True, null=True)

class Choice(BaseChoice):
schema = models.ForeignKey(Schema, related_name='choices')

class Attr(BaseAttribute):
schema = models.ForeignKey(Schema, related_name='attrs')
choice = models.ForeignKey(Choice, blank=True, null=True)

--------------------------------------------------------------

Andy

unread,
May 10, 2010, 7:33:40 AM5/10/10
to eav-django
Hi,
your code looks correct. One thing that must be fixed though is
Item.get_schemata_for_model: it has to be a class method which returns
schemata for a model (i.e. a Python class). Filtering by family is
related not to the class but to one of its objects (i.e. a Python
class instance). You should move the filtering code to
Item.get_schemata_for_instance, e.g.:

class Item(BaseEntity):
family = models.ForeignKey(Family, blank=True, null=True)
title = models.CharField(max_length=50)

@classmethod
def get_schemata_for_model(cls):
return Schema.objects.all()

def get_schemata_for_instance(self, schemata):
if self.family:
return schemata.filter(family=self.family)
return schemata # or schemata.none()

def __unicode__(self):
return self.title

(You would only need to filter schemata on the model level if there
were multiple EAV entity classes and a single schema class for them,
and the schemata had to be shown only for particular entity models.)

In short:

* get_schemata_for_model -- what schemata *may* be related to Item
objects?
* get_schemata_for_instance -- what schemata are related to given Item
object?

The adding/editing workflow for Item will depend on what
Item.get_schemata_for_instance returns when `family` is not defined:

a) no `family`, default queryset --> whole lot of attributes in admin,
of which only some will be preserved once you specify a family;
b) no `family`, empty queryset --> only normal fields in admin; fill
them in, click "Save", the `family` is saved, the list of schemata for
instance is now populated. If there are required schemata, the form is
considered invalid and is presented to you once again with certain EAV
attributes marked red.

The first option is better when you don't expect many schemata; the
second option is better when there are too many schemata for editor to
grok. In our web shop project we choose the second option.

Cheers,
Andy

llonchj

unread,
May 11, 2010, 12:30:10 AM5/11/10
to eav-django
Andy,

Thank you, it looks great!

Cheers,
Jordi
Reply all
Reply to author
Forward
0 new messages