from __future__ import unicode_literals from django.contrib.contenttypes.models import ContentType from django.db import models from django.db.models import Q from django.urls import reverse from image_cropping import ImageRatioField from orderable.models import Orderable from eav.models import Entity from icmgeneric.base import GenericPage from django.utils.translation import ugettext_lazy as _ class Product(GenericPage): name = models.CharField(max_length=256, verbose_name=_('Name')) annotation = models.TextField(null=True, blank=True, verbose_name=_('Annotation')) category = models.ForeignKey(Category, null=True, verbose_name=_('Category')) price = models.DecimalField(null=True, verbose_name=_('Price'), max_digits=6, decimal_places=2) old_price = models.DecimalField(null=True, blank=True, verbose_name=_('Old price'), max_digits=6, decimal_places=2) discount = models.IntegerField(null=True, blank=True, verbose_name=_('Discount')) in_new_products_list = models.BooleanField(default=False, verbose_name=_('In new products list')) in_stock = models.BooleanField(default=True, verbose_name=_('In stock')) active = models.BooleanField(default=True, verbose_name=_('Active')) @property def entity(self): return Entity(self) @property def values(self): return self.entity.get_values() def get_values_for_detail(self): dc = {} for value in self.values: if value.attribute.name not in dc.keys(): dc[value.attribute.name] = [value.value] else: dc[value.attribute.name].append(value.value) return dc def get_absolute_url(self): return reverse('catalog:detail', args=[self.category.slug, self.slug]) def get_attributes(self): attr_list = [] for item in self.values: attr_list.append(item.attribute) return attr_list def get_filters(self): return self.values.filter(Q(active=True) & Q(in_filter_list=True)) def get_filters_values(self): filters = [] for filter in self.values.filter(Q(active=True) & Q(in_filter_list=True)): filters.append(filter.value) return filters def get_photos(self): return self.photoitem_set.filter(active=True) def __unicode__(self): return self.name class Meta: ordering = ['name'] verbose_name = _('Product') verbose_name_plural = _('Products') eav.register(Product) these are property and value models for entities
class Attribute(models.Model): class Meta: ordering = ['content_type', 'name'] unique_together = ('site', 'content_type', 'slug') verbose_name, verbose_name_plural = _('Attribute'), _('Attributes') TYPE_TEXT = 'text' TYPE_FLOAT = 'float' TYPE_INT = 'int' TYPE_DATE = 'date' TYPE_BOOLEAN = 'bool' # TYPE_OBJECT = 'object' # TYPE_ENUM = 'enum' DATATYPE_CHOICES = ( (TYPE_TEXT, _(u"Text")), (TYPE_FLOAT, _(u"Float")), (TYPE_INT, _(u"Integer")), (TYPE_DATE, _(u"Date")), (TYPE_BOOLEAN, _(u"True / False")), # (TYPE_OBJECT, _(u"Django Object")), # (TYPE_ENUM, _(u"Multiple Choice")), ) name = models.CharField(max_length=100, help_text=_(u"User-friendly attribute name"), verbose_name=_(u'name')) content_type = models.ForeignKey(ContentType, blank=True, null=True, verbose_name=_(u"content type")) site = models.ForeignKey(Site, verbose_name=_(u"site"), default=settings.SITE_ID) slug = EavSlugField(max_length=50, db_index=True, help_text=_(u"Short unique attribute label"), verbose_name=_(u'slug')) description = models.CharField(max_length=256, blank=True, null=True, help_text=_(u"Short description"), verbose_name=_(u'description')) # enum_group = models.ForeignKey(EnumGroup, verbose_name=_(u"choice group"), blank=True, null=True) # type = models.CharField(_(u"type"), max_length=20, blank=True, null=True) @property def help_text(self): return self.description datatype = EavDatatypeField(max_length=6, choices=DATATYPE_CHOICES, verbose_name=_(u"data type")) created = models.DateTimeField(default=timezone.now, editable=False, verbose_name=_(u"created")) modified = models.DateTimeField(auto_now=True, verbose_name=_(u"modified")) required = models.BooleanField(default=False, verbose_name=_(u"required")) display_order = models.PositiveIntegerField(default=1, verbose_name=_(u"display order")) active = models.BooleanField(default=True, verbose_name=_('Active')) objects = models.Manager() on_site = CurrentSiteManager() def get_validators(self): ''' Returns the appropriate validator function from :mod:`~eav.validators` as 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. ''' DATATYPE_VALIDATORS = { 'text': validate_text, 'float': validate_float, 'int': validate_int, 'date': validate_date, 'bool': validate_bool, 'object': validate_object, 'enum': validate_enum, } validation_function = DATATYPE_VALIDATORS[self.datatype] return [validation_function] # def validate_value(self, value): # ''' # Check *value* against the validators returned by # :meth:`get_validators` for this attribute. # ''' # for validator in self.get_validators(): # validator(value) # if self.datatype == self.TYPE_ENUM: # if value not in self.enum_group.enums.all(): # raise ValidationError(_(u"%(enum)s is not a valid choice " # u"for %(attr)s") % \ # {'enum': value, 'attr': self}) def save(self, *args, **kwargs): ''' Saves the Attribute and auto-generates a slug field if one wasn't provided. ''' if not self.slug: self.slug = EavSlugField.create_slug_from_name(self.name) self.full_clean() super(Attribute, self).save(*args, **kwargs) # def clean(self): # ''' # Validates the attribute. Will raise ``ValidationError`` if # 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. # ''' # if self.datatype == self.TYPE_ENUM and not self.enum_group: # raise ValidationError(_( # u"You must set the choice group for multiple choice" \ # u"attributes")) # # if self.datatype != self.TYPE_ENUM and self.enum_group: # raise ValidationError(_( # u"You can only assign a choice group to multiple choice " \ # u"attributes")) # def get_choices(self): # ''' # Returns a query set of :class:`EnumValue` objects for this attribute. # Returns None if the datatype of this attribute is not *TYPE_ENUM*. # ''' # if not self.datatype == Attribute.TYPE_ENUM: # return None # return self.enum_group.enums.all() def save_value(self, entity, value): ''' Called with *entity*, any django object registered with eav, and *value*, the :class:`Value` this attribute for *entity* should be set to. If a :class:`Value` object for this *entity* and attribute doesn't exist, one will be created. .. note:: If *value* is None and a :class:`Value` object exists for this Attribute and *entity*, it will delete that :class:`Value` object. ''' ct = ContentType.objects.get_for_model(entity) try: value_obj = self.value_set.get(entity_ct=ct, entity_id=entity.pk, attribute=self) except Value.DoesNotExist: if value == None or value == '': return value_obj = Value.objects.create(entity_ct=ct, entity_id=entity.pk, attribute=self) if value == None or value == '': value_obj.delete() return if value != value_obj.value: value_obj.value = value value_obj.save() def __unicode__(self): return u"%s.%s (%s)" % (self.content_type, self.name, self.get_datatype_display()) class Value(models.Model): entity_ct = models.ForeignKey(ContentType, related_name='value_entities', verbose_name=_(u'Entity')) entity_id = models.IntegerField(verbose_name=_(u'Entity Code')) entity = generic.GenericForeignKey(ct_field='entity_ct', fk_field='entity_id') attribute = models.ForeignKey(Attribute, db_index=True, verbose_name=_(u"Attribute")) value_text = models.TextField(blank=True, null=True, verbose_name=_(u"Text Value")) value_float = models.FloatField(blank=True, null=True, verbose_name=_(u"Float Value")) value_int = models.IntegerField(blank=True, null=True, verbose_name=_(u"Integer Value")) value_date = models.DateTimeField(blank=True, null=True, verbose_name=_(u"Date Value")) value_bool = models.NullBooleanField(blank=True, null=True, verbose_name=_(u"True/False Value")) # value_enum = models.ForeignKey(EnumValue, blank=True, null=True, related_name='eav_values') # generic_value_id = models.IntegerField(blank=True, null=True) # generic_value_ct = models.ForeignKey(ContentType, blank=True, null=True, related_name='value_values') # value_object = generic.GenericForeignKey(ct_field='generic_value_ct', fk_field='generic_value_id') created = models.DateTimeField(_(u"created"), default=timezone.now) modified = models.DateTimeField(_(u"modified"), auto_now=True) active = models.BooleanField(default=True, verbose_name=_('Active')) in_filter_list = models.BooleanField(default=False, verbose_name=_('In filter list')) def save(self, *args, **kwargs): ''' Validate and save this value ''' self.full_clean() super(Value, self).save(*args, **kwargs) # def clean(self): # ''' # Raises ``ValidationError`` if this value's attribute is *TYPE_ENUM* # and value_enum is not a valid choice for this value's attribute. # ''' # if self.attribute.datatype == Attribute.TYPE_ENUM and \ # self.value_enum: # if self.value_enum not in self.attribute.enum_group.enums.all(): # raise ValidationError(_(u"%(choice)s is not a valid " \ # u"choice for %s(attribute)") % \ # {'choice': self.value_enum, # 'attribute': self.attribute}) def get_count(self): dc = {'eav__' + self.attribute.slug: self.value} return self.entity.__class__.objects.filter(Q(**dc)).count() def _get_value(self): ''' Return the python object this value is holding ''' return getattr(self, 'value_%s' % self.attribute.datatype) def _set_value(self, new_value): ''' Set the object this value is holding ''' setattr(self, 'value_%s' % self.attribute.datatype, new_value) value = property(_get_value, _set_value) def __unicode__(self): return u"%s - %s: \"%s\"" % (self.entity, self.attribute.name, self.value) class Meta: verbose_name, verbose_name_plural = _(u'Value'), _(u'Values') Added a color and type property to the admin panel for the Product
Then I make for example such a query:
qs = Product.objects.filter(Q(name='name_1') & Q(name='name_2')) And does not find a single object. Tell me what's wrong.