diff --git a/eav/admin.py b/eav/admin.py index c7ab77d..8a0c1a4 100644 --- a/eav/admin.py +++ b/eav/admin.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # vim: ai ts=4 sts=4 et sw=4 coding=utf-8 # -# This software is derived from EAV-Django originally written and +# This software is derived from EAV-Django originally written and # copyrighted by Andrey Mikhaylenko # # This is free software: you can redistribute it and/or modify @@ -28,7 +28,7 @@ from django.utils.safestring import mark_safe from .models import Attribute, Value, EnumValue, EnumGroup class BaseEntityAdmin(ModelAdmin): - + def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None): """ Wrapper for ModelAdmin.render_change_form. Replaces standard static @@ -94,7 +94,7 @@ class BaseEntityInline(InlineModelAdmin): return [(None, {'fields': form.fields.keys()})] class AttributeAdmin(ModelAdmin): - list_display = ('name', 'slug', 'datatype', 'description', 'site') + list_display = ('name', 'content_type', 'slug', 'datatype', 'description', 'site') list_filter = ['site'] prepopulated_fields = {'slug': ('name',)} diff --git a/eav/fields.py b/eav/fields.py index 94e9740..8f7c9c7 100644 --- a/eav/fields.py +++ b/eav/fields.py @@ -82,9 +82,10 @@ class EavDatatypeField(models.CharField): :class:`~eav.models.Value` objects. ''' super(EavDatatypeField, self).validate(value, instance) - from .models import Attribute if not instance.pk: return + if type(instance).objects.get(pk=instance.pk).datatype == instance.datatype: + return if instance.value_set.count(): raise ValidationError(_(u"You cannot change the datatype of an " u"attribute that is already in use.")) diff --git a/eav/migrations/0002_auto_20161014_0157.py b/eav/migrations/0002_auto_20161014_0157.py new file mode 100644 index 0000000..70c3bd9 --- /dev/null +++ b/eav/migrations/0002_auto_20161014_0157.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.10 on 2016-10-13 16:57 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('eav', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='attribute', + options={'ordering': ['content_type', 'name']}, + ), + migrations.AddField( + model_name='attribute', + name='content_type', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType', verbose_name='content type'), + ), + migrations.AddField( + model_name='attribute', + name='display_order', + field=models.PositiveIntegerField(default=1, verbose_name='display order'), + ), + migrations.AlterUniqueTogether( + name='attribute', + unique_together=set([('site', 'content_type', 'slug')]), + ), + ] diff --git a/eav/models.py b/eav/models.py index 788f5a8..2c6aee0 100644 --- a/eav/models.py +++ b/eav/models.py @@ -152,8 +152,8 @@ class Attribute(models.Model): ''' class Meta: - ordering = ['name'] - unique_together = ('site', 'slug') + ordering = ['content_type', 'name'] + unique_together = ('site', 'content_type', 'slug') TYPE_TEXT = 'text' TYPE_FLOAT = 'float' @@ -176,6 +176,10 @@ class Attribute(models.Model): name = models.CharField(_(u"name"), max_length=100, help_text=_(u"User-friendly attribute 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) @@ -205,6 +209,8 @@ class Attribute(models.Model): required = models.BooleanField(_(u"required"), default=False) + display_order = models.PositiveIntegerField(_(u"display order"), default=1) + objects = models.Manager() on_site = CurrentSiteManager() @@ -312,7 +318,7 @@ class Attribute(models.Model): value_obj.save() def __unicode__(self): - return u"%s (%s)" % (self.name, self.get_datatype_display()) + return u"%s.%s (%s)" % (self.content_type, self.name, self.get_datatype_display()) class Value(models.Model): @@ -442,18 +448,19 @@ class Entity(object): Return a query set of all :class:`Attribute` objects that can be set for this entity. ''' - return self.model._eav_config_cls.get_attributes() + return self.model._eav_config_cls.get_attributes().filter( + models.Q(content_type__isnull=True) | models.Q(content_type=self.ct)).order_by('display_order') def _hasattr(self, attribute_slug): ''' - Since we override __getattr__ with a backdown to the database, this exists as a way of + Since we override __getattr__ with a backdown to the database, this exists as a way of checking whether a user has set a real attribute on ourselves, without going to the db if not ''' return attribute_slug in self.__dict__ def _getattr(self, attribute_slug): ''' - Since we override __getattr__ with a backdown to the database, this exists as a way of + Since we override __getattr__ with a backdown to the database, this exists as a way of getting the value a user set for one of our attributes, without going to the db to check ''' return self.__dict__[attribute_slug] @@ -482,7 +489,7 @@ class Entity(object): value = self._getattr(attribute.slug) else: value = values_dict.get(attribute.slug, None) - + if value is None: if attribute.required: raise ValidationError(_(u"%(attr)s EAV field cannot " \ @@ -495,7 +502,7 @@ class Entity(object): raise ValidationError(_(u"%(attr)s EAV field %(err)s") % \ {'attr': attribute.slug, 'err': e}) - + def get_values_dict(self): values_dict = dict() for value in self.get_values():