From 015e399a8111fafbe09adc44abda3efca80bd6b8 Mon Sep 17 00:00:00 2001 From: Thomas Kremmel Date: Fri, 7 Sep 2012 09:59:29 +0200 Subject: [PATCH 01/19] initial tests --- eav/registry.py | 1 + 1 file changed, 1 insertion(+) diff --git a/eav/registry.py b/eav/registry.py index a5265af..03cd0fe 100644 --- a/eav/registry.py +++ b/eav/registry.py @@ -71,6 +71,7 @@ def register(model_cls, config_cls=None): .. note:: Multiple registrations for the same entity are harmlessly ignored. ''' + print "in registry - stateradio fork" if hasattr(model_cls, '_eav_config_cls'): return From c151fd28e73b06c39e49703e5bf25a691d2fe512 Mon Sep 17 00:00:00 2001 From: Thomas Kremmel Date: Fri, 7 Sep 2012 10:16:50 +0200 Subject: [PATCH 02/19] setup.py modified --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index df3c7ef..6f01a99 100755 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ 'Django app.', long_description=open('README.rst').read(), - url='http://github.com/mvpdev/django-eav', + url='http://github.com/mr-stateradio/django-eav', packages=['eav'], From c3a7f909225695b637f431b8d5db14d6d0671518 Mon Sep 17 00:00:00 2001 From: Thomas Kremmel Date: Fri, 7 Sep 2012 16:22:21 +0200 Subject: [PATCH 03/19] added feature to restrict scope of eav attributes to arbitrary models referenced via a generic FK --- eav/forms.py | 1 - eav/models.py | 26 ++++++++++++++++++++++---- eav/registry.py | 1 - 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/eav/forms.py b/eav/forms.py index 26e5785..c9b4526 100644 --- a/eav/forms.py +++ b/eav/forms.py @@ -63,7 +63,6 @@ def __init__(self, data=None, *args, **kwargs): def _build_dynamic_fields(self): # reset form fields self.fields = deepcopy(self.base_fields) - for attribute in self.entity.get_all_attributes(): value = getattr(self.entity, attribute.slug) diff --git a/eav/models.py b/eav/models.py index 350d8f7..af92cad 100644 --- a/eav/models.py +++ b/eav/models.py @@ -118,6 +118,9 @@ class Attribute(models.Model): to save or create any entity object for which this attribute applies, without first setting this EAV attribute. + The generic restriction_object field enables the user to restrict the scope + of the attribute to an arbitrary model, referenced by ID and Content Type + There are 7 possible values for datatype: * int (TYPE_INT) @@ -190,6 +193,14 @@ class Meta: type = models.CharField(_(u"type"), max_length=20, blank=True, null=True) + generic_restriction_id = models.IntegerField(blank=True, null=True) + generic_restriction_ct = models.ForeignKey(ContentType, blank=True, + null=True, + related_name='attribute_restrictions') + restriction_object = generic.GenericForeignKey( + ct_field='generic_restriction_ct', + fk_field='generic_restriction_id') + @property def help_text(self): return self.description @@ -337,7 +348,6 @@ class Value(models.Model): class Meta: unique_together = ('entity_ct', 'entity_id', 'attribute') - entity_ct = models.ForeignKey(ContentType, related_name='value_entities') entity_id = models.IntegerField() entity = generic.GenericForeignKey(ct_field='entity_ct', @@ -411,14 +421,19 @@ class Entity(object): def __init__(self, instance): ''' Set self.model equal to the instance of the model that we're attached - to. Also, store the content type of that instance. + to. Also, store the content type of that instance and the restriction + attribute of the model if it has been restricted. ''' self.model = instance self.ct = ContentType.objects.get_for_model(instance) + if hasattr(instance, 'eav_restriction_object'): + self.restriction = instance.eav_restriction_object + else: + self.restriction = None def __getattr__(self, name): ''' - Tha magic getattr helper. This is called whenevery you do + Tha magic getattr helper. This is called whenever you do this_instance. Checks if *name* is a valid slug for attributes available to this @@ -445,7 +460,10 @@ def get_all_attributes(self): Return a query set of all :class:`Attribute` objects that can be set for this entity. ''' - return self.model._eav_config_cls.get_attributes() + if self.restriction is not None: + return self.model._eav_config_cls.get_attributes(self.restriction) + else: + return self.model._eav_config_cls.get_attributes() def save(self): ''' diff --git a/eav/registry.py b/eav/registry.py index 03cd0fe..a5265af 100644 --- a/eav/registry.py +++ b/eav/registry.py @@ -71,7 +71,6 @@ def register(model_cls, config_cls=None): .. note:: Multiple registrations for the same entity are harmlessly ignored. ''' - print "in registry - stateradio fork" if hasattr(model_cls, '_eav_config_cls'): return From 433ebea615c38e0f1e70234f64ef48e65bed4222 Mon Sep 17 00:00:00 2001 From: Thomas Kremmel Date: Mon, 10 Sep 2012 11:43:59 +0200 Subject: [PATCH 04/19] added README information for this fork --- README.rst | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/README.rst b/README.rst index c0aac2d..5e1232c 100644 --- a/README.rst +++ b/README.rst @@ -26,6 +26,89 @@ check `Wikipedia * This app has slightly more robust (but still not perfect) filtering. +This Fork +--------- + +This fork adds a feature to restrict the scope of attributes. Django-eav only +provides the possibility to enable attributes for selected models using advanced +registration: + + >>> class MyEavConfigClass(EavConfig): + >>> @classmethod + >>> def get_attributes(cls): + >>> return Attribute.objects.filter(type='person') + + >>> eav.register(MyModel, MyEavConfigClass) + +The feature added with this fork enhances the possibility to restrict the scope +of attributes to arbitrary models referenced by the registered model. + +The registered model has to implement one additional attribute and the configuration +class has to consider this attribute in the get_attributes method. See example source +code below: + +A use case for this feature could be: + +A generic motorbike retail backend which implements a motorbike model having a +ForeignKey to a manufacturer. The backend allows to add custom fields to the motorbike +model via django-eav. Additionally the backend allows the user to add custom +attributes which are not only restricted to the model motorbike, but also restricted +to the motorbike manufacturer, allowing the user to define manufacturer specific +custom attributes. + +Source code for use case: + +model Definition: + + >>> class Motorbike(models.Model): + >>> """ + >>> Representation of a motorbike + >>> """ + >>> name = models.CharField(max_length=100) + >>> manufacturer = models.ForeignKey(Manufacturer) + >>> + >>> 'add eav restrictions - enables eav to restrict attributes to defined foreignkeys' + >>> eav_restriction_object = models.ForeignKey(Manufacturer, blank=True, editable=False, + related_name='eav_attribute_restrictions') + + >>> def save(self, *args, **kwargs): + >>> self.eav_restriction_object=self.manufacturer + >>> super(Motorbike, self).save(*args, **kwargs) + +Config Class Definition: + + >>> class EavTaskConfigClass(EavConfig): + >>> """ + >>> configClass to restrict the created attributes to motorbikes assigned to a speific + >>> maufacturer + >>> """ + >>> + >>> @classmethod + >>> def get_attributes(cls, eavRestriction=None): + >>> """ + >>> return the attributes and consider the provided eavRestriction + >>> the eavRestriction is a ForeignKey to an arbitrary model instance + >>> """ + >>> if eavRestriction is not None: + >>> #return only attributes which have their restriction_object set to the provided eavRestriction + >>> eavRestrictionType = ContentType.objects.get_for_model(eavRestriction) + >>> return Attribute.objects.filter(type='Motorbike', generic_restriction_id=eavRestriction.id, generic_restriction_ct__pk=eavRestrictionType.id) + >>> else: + >>> return Attribute.objects.filter(type='Motorbike') + >>> + >>> eav.register(Motorbike, EavTaskConfigClass) + +Attribute Filtering: + +Since the eav internal restriction_object is implemented as a GenericForeignKey to support arbitrary +eav_restriction_object in a registered model, filtering is slightly more complex and has to be done +using the ContentType model. + + >>> from django.contrib.contenttypes.models import ContentType + >>> listTypeCT = ContentType.objects.get_for_model(listType) + >>> Attribute.objects.filter(type=listType.model, generic_restriction_id=manfacturerInstance.id, generic_restriction_ct__pk=listTypeCT.id) + + Installation ------------ From a61340c336d63c03b195b3ac6113e8cf77579ff8 Mon Sep 17 00:00:00 2001 From: Thomas Kremmel Date: Mon, 10 Sep 2012 11:49:14 +0200 Subject: [PATCH 05/19] readme layout fix --- README.rst | 72 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/README.rst b/README.rst index 5e1232c..d2b94c0 100644 --- a/README.rst +++ b/README.rst @@ -60,43 +60,46 @@ Source code for use case: model Definition: - >>> class Motorbike(models.Model): - >>> """ - >>> Representation of a motorbike - >>> """ - >>> name = models.CharField(max_length=100) - >>> manufacturer = models.ForeignKey(Manufacturer) - >>> - >>> 'add eav restrictions - enables eav to restrict attributes to defined foreignkeys' - >>> eav_restriction_object = models.ForeignKey(Manufacturer, blank=True, editable=False, + class Motorbike(models.Model): + """ + Representation of a motorbike + """ + name = models.CharField(max_length=100) + manufacturer = models.ForeignKey(Manufacturer) + + 'add eav restrictions - enables eav to restrict attributes to defined foreignkeys' + eav_restriction_object = models.ForeignKey(Manufacturer, blank=True, editable=False, related_name='eav_attribute_restrictions') - - >>> def save(self, *args, **kwargs): - >>> self.eav_restriction_object=self.manufacturer - >>> super(Motorbike, self).save(*args, **kwargs) + + def save(self, *args, **kwargs): + self.eav_restriction_object=self.manufacturer + super(Motorbike, self).save(*args, **kwargs) Config Class Definition: - >>> class EavTaskConfigClass(EavConfig): - >>> """ - >>> configClass to restrict the created attributes to motorbikes assigned to a speific - >>> maufacturer - >>> """ - >>> - >>> @classmethod - >>> def get_attributes(cls, eavRestriction=None): - >>> """ - >>> return the attributes and consider the provided eavRestriction - >>> the eavRestriction is a ForeignKey to an arbitrary model instance - >>> """ - >>> if eavRestriction is not None: - >>> #return only attributes which have their restriction_object set to the provided eavRestriction - >>> eavRestrictionType = ContentType.objects.get_for_model(eavRestriction) - >>> return Attribute.objects.filter(type='Motorbike', generic_restriction_id=eavRestriction.id, generic_restriction_ct__pk=eavRestrictionType.id) - >>> else: - >>> return Attribute.objects.filter(type='Motorbike') - >>> - >>> eav.register(Motorbike, EavTaskConfigClass) + class EavTaskConfigClass(EavConfig): + """ + configClass to restrict the created attributes to motorbikes assigned to a speific + maufacturer + """ + + @classmethod + def get_attributes(cls, eavRestriction=None): + """ + return the attributes and consider the provided eavRestriction + the eavRestriction is a ForeignKey to an arbitrary model instance + """ + if eavRestriction is not None: + #return only attributes which have their restriction_object set to the provided + #eavRestriction + eavRestrictionType = ContentType.objects.get_for_model(eavRestriction) + return Attribute.objects.filter(type='Motorbike', + generic_restriction_id=eavRestriction.id, + generic_restriction_ct__pk=eavRestrictionType.id) + else: + return Attribute.objects.filter(type='Motorbike') + + eav.register(Motorbike, EavTaskConfigClass) Attribute Filtering: @@ -106,7 +109,8 @@ using the ContentType model. >>> from django.contrib.contenttypes.models import ContentType >>> listTypeCT = ContentType.objects.get_for_model(listType) - >>> Attribute.objects.filter(type=listType.model, generic_restriction_id=manfacturerInstance.id, generic_restriction_ct__pk=listTypeCT.id) + >>> Attribute.objects.filter(type=listType.model, generic_restriction_id=manfacturerInstance.id, + generic_restriction_ct__pk=listTypeCT.id) Installation From 39c8f8aa0f4a65f358dc13d9aafceea370d30583 Mon Sep 17 00:00:00 2001 From: Thomas Kremmel Date: Mon, 10 Sep 2012 11:52:36 +0200 Subject: [PATCH 06/19] readme layout fix --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index d2b94c0..1d3a931 100644 --- a/README.rst +++ b/README.rst @@ -58,7 +58,7 @@ custom attributes. Source code for use case: -model Definition: +model Definition: :: class Motorbike(models.Model): """ From 63fa49535f64e1cc653d6b87d6c67b98499874e6 Mon Sep 17 00:00:00 2001 From: Thomas Kremmel Date: Mon, 10 Sep 2012 11:53:43 +0200 Subject: [PATCH 07/19] readme layout fix --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 1d3a931..6624769 100644 --- a/README.rst +++ b/README.rst @@ -31,7 +31,7 @@ This Fork This fork adds a feature to restrict the scope of attributes. Django-eav only provides the possibility to enable attributes for selected models using advanced -registration: +registration: :: >>> class MyEavConfigClass(EavConfig): >>> @classmethod @@ -75,7 +75,7 @@ model Definition: :: self.eav_restriction_object=self.manufacturer super(Motorbike, self).save(*args, **kwargs) -Config Class Definition: +Config Class Definition: :: class EavTaskConfigClass(EavConfig): """ @@ -105,7 +105,7 @@ Attribute Filtering: Since the eav internal restriction_object is implemented as a GenericForeignKey to support arbitrary eav_restriction_object in a registered model, filtering is slightly more complex and has to be done -using the ContentType model. +using the ContentType model. :: >>> from django.contrib.contenttypes.models import ContentType >>> listTypeCT = ContentType.objects.get_for_model(listType) From 5031c979acefb4f4044bbd8dae2802ba551dc00d Mon Sep 17 00:00:00 2001 From: Thomas Kremmel Date: Mon, 10 Sep 2012 12:00:37 +0200 Subject: [PATCH 08/19] restrict README to only include information for this fork --- README.rst | 91 ++++++------------------------------------------------ 1 file changed, 10 insertions(+), 81 deletions(-) diff --git a/README.rst b/README.rst index 6624769..73a685c 100644 --- a/README.rst +++ b/README.rst @@ -5,29 +5,14 @@ django-eav Introduction ------------ -django-eav provides an Entity-Attribute-Value storage model for django apps. - -For a decent explanation of what an Entity-Attribute-Value storage model is, -check `Wikipedia -`_. - -.. note:: - This software was inspired / derived from the excellent `eav-django - `_ written by Andrey - Mikhaylenko. - - There are a few notable differences between this implementation and the - eav-django implementation. - - * This one is called django-eav, whereas the other is called eav-django. - * This app allows you to to 'attach' EAV attributes to any existing django - model (even from third-party apps) without making any changes to the those - models' code. - * This app has slightly more robust (but still not perfect) filtering. - - -This Fork ---------- +This is a fork from the repository: https://github.com/mvpdev/django-eav +Please read the README their to understand the purpose of this repository. + +This README only explains the feature added in this fork: + + +Feature added in this fork: +--------------------------- This fork adds a feature to restrict the scope of attributes. Django-eav only provides the possibility to enable attributes for selected models using advanced @@ -118,63 +103,7 @@ Installation From Github ~~~~~~~~~~~ -You can install django-eav directly from guthub:: - - pip install -e git+git://github.com/mvpdev/django-eav.git#egg=django-eav - -Usage ------ - -Edit settings.py -~~~~~~~~~~~~~~~~ -Add ``eav`` to your ``INSTALLED_APPS`` in your project's ``settings.py`` file. - -Register your model(s) -~~~~~~~~~~~~~~~~~~~~~~ -Before you can attach eav attributes to your model, you must register your -model with eav:: - - >>> import eav - >>> eav.register(MyModel) - -Generally you would do this in your ``models.py`` immediate after your model -declarations. - -Create some attributes -~~~~~~~~~~~~~~~~~~~~~~ -:: - - >>> from eav.models import Attribute - >>> Attribute.objects.create(name='Weight', datatype=Attribute.TYPE_FLOAT) - >>> Attribute.objects.create(name='Color', datatype=Attribute.TYPE_TEXT) - - -Assign eav values -~~~~~~~~~~~~~~~~~ -:: - - >>> m = MyModel() - >>> m.eav.weight = 15.4 - >>> m.eav.color = 'blue' - >>> m.save() - >>> m = MyModel.objects.get(pk=m.pk) - >>> m.eav.weight - 15.4 - >>> m.eav.color - blue - - >>> p = MyModel.objects.create(eav__weight = 12, eav__color='red') - -Filter on eav values -~~~~~~~~~~~~~~~~~~~~ -:: - - >>> MyModel.objects.filter(eav__weight=15.4) - - >>> MyModel.objects.exclude(name='bob', eav__weight=15.4, eav__color='red') - +You can install this django-eav fork directly from guthub:: -Documentation and Examples --------------------------- + pip install -e git+git://github.com/mr-stateradio/django-eav.git#egg=django-eav -``_ From bec4f8acef96eb3ff7d84bf64378cf0bea8e9a5c Mon Sep 17 00:00:00 2001 From: Thomas Kremmel Date: Mon, 10 Sep 2012 12:03:56 +0200 Subject: [PATCH 09/19] completed filtering example --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 73a685c..ec2efc7 100644 --- a/README.rst +++ b/README.rst @@ -93,9 +93,9 @@ eav_restriction_object in a registered model, filtering is slightly more complex using the ContentType model. :: >>> from django.contrib.contenttypes.models import ContentType - >>> listTypeCT = ContentType.objects.get_for_model(listType) - >>> Attribute.objects.filter(type=listType.model, generic_restriction_id=manfacturerInstance.id, - generic_restriction_ct__pk=listTypeCT.id) + >>> manufacturerCT = ContentType.objects.get_for_model(manufacturerInstance) + >>> Attribute.objects.filter(type="motorbike", generic_restriction_id=manfacturerInstance.id, + generic_restriction_ct__pk=manufacturerCT.id) Installation From 06beed52d62162963e5288f0abe8a4cb66919fd4 Mon Sep 17 00:00:00 2001 From: Thomas Kremmel Date: Mon, 10 Sep 2012 12:07:13 +0200 Subject: [PATCH 10/19] udpated readme --- README.rst | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index ec2efc7..5cf4044 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,7 @@ django-eav Introduction ------------ -This is a fork from the repository: https://github.com/mvpdev/django-eav +This is a fork from the repository: https://github.com/mvpdev/django-eav . Please read the README their to understand the purpose of this repository. This README only explains the feature added in this fork: @@ -18,12 +18,12 @@ This fork adds a feature to restrict the scope of attributes. Django-eav only provides the possibility to enable attributes for selected models using advanced registration: :: - >>> class MyEavConfigClass(EavConfig): - >>> @classmethod - >>> def get_attributes(cls): - >>> return Attribute.objects.filter(type='person') + class MyEavConfigClass(EavConfig): + @classmethod + def get_attributes(cls): + return Attribute.objects.filter(type='person') - >>> eav.register(MyModel, MyEavConfigClass) + eav.register(MyModel, MyEavConfigClass) The feature added with this fork enhances the possibility to restrict the scope of attributes to arbitrary models referenced by the registered model. @@ -38,8 +38,10 @@ A generic motorbike retail backend which implements a motorbike model having a ForeignKey to a manufacturer. The backend allows to add custom fields to the motorbike model via django-eav. Additionally the backend allows the user to add custom attributes which are not only restricted to the model motorbike, but also restricted -to the motorbike manufacturer, allowing the user to define manufacturer specific -custom attributes. +to the motorbike manufacturer, allowing the user to add manufacturer specific fields +to the motorbike model. + +Custom attributes. Source code for use case: From 370c55449d17114a54ee83debc54b42a521cfa02 Mon Sep 17 00:00:00 2001 From: Thomas Kremmel Date: Mon, 10 Sep 2012 12:08:31 +0200 Subject: [PATCH 11/19] updated readme --- README.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/README.rst b/README.rst index 5cf4044..3bcb0b2 100644 --- a/README.rst +++ b/README.rst @@ -41,7 +41,6 @@ attributes which are not only restricted to the model motorbike, but also restri to the motorbike manufacturer, allowing the user to add manufacturer specific fields to the motorbike model. -Custom attributes. Source code for use case: From d15acad650601cb5e04586ebb9a9f54db32a537d Mon Sep 17 00:00:00 2001 From: Thomas Kremmel Date: Mon, 10 Sep 2012 12:08:59 +0200 Subject: [PATCH 12/19] updated readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 3bcb0b2..52ca12f 100644 --- a/README.rst +++ b/README.rst @@ -44,7 +44,7 @@ to the motorbike model. Source code for use case: -model Definition: :: +Model Definition: :: class Motorbike(models.Model): """ From cab70f775bae8045b6f8d8383ea4c221e8c6c495 Mon Sep 17 00:00:00 2001 From: Thomas Kremmel Date: Wed, 16 Jan 2013 12:01:38 +0100 Subject: [PATCH 13/19] enabled edit of attributes for certain model fields --- eav/fields.py | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/eav/fields.py b/eav/fields.py index 94e9740..10e9d9b 100644 --- a/eav/fields.py +++ b/eav/fields.py @@ -81,10 +81,32 @@ def validate(self, value, instance): :class:`~eav.models.Attribute` that is already used by :class:`~eav.models.Value` objects. ''' + print "in eav validate EavDatatypeField" + print value + print instance + print instance.datatype + print instance.required super(EavDatatypeField, self).validate(value, instance) from .models import Attribute if not instance.pk: return - if instance.value_set.count(): - raise ValidationError(_(u"You cannot change the datatype of an " - u"attribute that is already in use.")) + else: + #raise validation error if save command tries to change fields which are not subject to change + old_instance = Attribute.objects.get(pk=instance.pk) + if old_instance.datatype != instance.datatype: + raise ValidationError(_(u"You cannot change the datatype of an" + u"attribute.")) + elif old_instance.datatype != instance.datatype: + raise ValidationError(_(u"You cannot change the type of an" + u"attribute.")) + elif old_instance.generic_restriction_id != instance.generic_restriction_id: + raise ValidationError(_(u"You cannot change the generic" + u"restriction of an attribute.")) + elif old_instance.generic_restriction_ct != instance.generic_restriction_ct: + raise ValidationError(_(u"You cannot change the generic restriction content type of an attribute.")) + elif old_instance.slug != instance.slug: + raise ValidationError(_(u"You cannot change the slug of an attribute.")) + elif old_instance.name != instance.name: + raise ValidationError(_(u"You cannot change the name of an attribute.")) + else: + return \ No newline at end of file From fe83d9f3587486078a44846ac8ab6f78000ae9ff Mon Sep 17 00:00:00 2001 From: Thomas Kremmel Date: Wed, 16 Jan 2013 12:03:28 +0100 Subject: [PATCH 14/19] clean up --- eav/fields.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/eav/fields.py b/eav/fields.py index 10e9d9b..b17d7cb 100644 --- a/eav/fields.py +++ b/eav/fields.py @@ -81,11 +81,6 @@ def validate(self, value, instance): :class:`~eav.models.Attribute` that is already used by :class:`~eav.models.Value` objects. ''' - print "in eav validate EavDatatypeField" - print value - print instance - print instance.datatype - print instance.required super(EavDatatypeField, self).validate(value, instance) from .models import Attribute if not instance.pk: @@ -109,4 +104,4 @@ def validate(self, value, instance): elif old_instance.name != instance.name: raise ValidationError(_(u"You cannot change the name of an attribute.")) else: - return \ No newline at end of file + return From 4dd67f1524aca5ab4204406f6a35f0836e6402b5 Mon Sep 17 00:00:00 2001 From: Thomas Kremmel Date: Mon, 21 Jan 2013 18:01:49 +0100 Subject: [PATCH 15/19] set model type for date type to models.DateField and removed models.DateTimeField --- eav/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eav/models.py b/eav/models.py index b07d26f..4fe2ba3 100644 --- a/eav/models.py +++ b/eav/models.py @@ -356,7 +356,7 @@ class Meta: value_text = models.TextField(blank=True, null=True) value_float = models.FloatField(blank=True, null=True) value_int = models.IntegerField(blank=True, null=True) - value_date = models.DateTimeField(blank=True, null=True) + value_date = models.DateField(blank=True, null=True) value_bool = models.NullBooleanField(blank=True, null=True) value_enum = models.ForeignKey(EnumValue, blank=True, null=True, related_name='eav_values') From ccfa5c484ecbcb70f548e28bab897dd943e22d1e Mon Sep 17 00:00:00 2001 From: Thomas Kremmel Date: Mon, 21 Jan 2013 22:13:47 +0100 Subject: [PATCH 16/19] added datetime field --- eav/forms.py | 7 ++++--- eav/models.py | 9 +++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/eav/forms.py b/eav/forms.py index c9b4526..b4111aa 100644 --- a/eav/forms.py +++ b/eav/forms.py @@ -28,7 +28,7 @@ ''' from copy import deepcopy -from django.forms import BooleanField, CharField, DateTimeField, FloatField, \ +from django.forms import BooleanField, CharField, DateField, DateTimeField, FloatField, \ IntegerField, ModelForm, ChoiceField, ValidationError from django.contrib.admin.widgets import AdminSplitDateTime from django.utils.translation import ugettext_lazy as _ @@ -49,7 +49,8 @@ class BaseDynamicEntityForm(ModelForm): 'text': CharField, 'float': FloatField, 'int': IntegerField, - 'date': DateTimeField, + 'date': DateField, + 'datetime': DateTimeField, 'bool': BooleanField, 'enum': ChoiceField, } @@ -84,7 +85,7 @@ def _build_dynamic_fields(self): if value: defaults.update({'initial': value.pk}) - elif datatype == attribute.TYPE_DATE: + elif datatype == attribute.TYPE_DATE_TIME: defaults.update({'widget': AdminSplitDateTime}) elif datatype == attribute.TYPE_OBJECT: continue diff --git a/eav/models.py b/eav/models.py index 4fe2ba3..51b9ede 100644 --- a/eav/models.py +++ b/eav/models.py @@ -121,12 +121,13 @@ class Attribute(models.Model): The generic restriction_object field enables the user to restrict the scope of the attribute to an arbitrary model, referenced by ID and Content Type - There are 7 possible values for datatype: + There are 8 possible values for datatype: * int (TYPE_INT) * float (TYPE_FLOAT) * text (TYPE_TEXT) * date (TYPE_DATE) + * datetime (TYPE_DATE_TIME) * bool (TYPE_BOOLEAN) * object (TYPE_OBJECT) * enum (TYPE_ENUM) @@ -161,6 +162,7 @@ class Meta: TYPE_FLOAT = 'float' TYPE_INT = 'int' TYPE_DATE = 'date' + TYPE_DATE_TIME = 'datetime' TYPE_BOOLEAN = 'bool' TYPE_OBJECT = 'object' TYPE_ENUM = 'enum' @@ -170,6 +172,7 @@ class Meta: (TYPE_FLOAT, _(u"Float")), (TYPE_INT, _(u"Integer")), (TYPE_DATE, _(u"Date")), + (TYPE_DATE_TIME, _(u"Date and Time")), (TYPE_BOOLEAN, _(u"True / False")), (TYPE_OBJECT, _(u"Django Object")), (TYPE_ENUM, _(u"Multiple Choice")), @@ -205,7 +208,7 @@ class Meta: def help_text(self): return self.description - datatype = EavDatatypeField(_(u"data type"), max_length=6, + datatype = EavDatatypeField(_(u"data type"), max_length=8, choices=DATATYPE_CHOICES) created = models.DateTimeField(_(u"created"), default=datetime.now, @@ -233,6 +236,7 @@ def get_validators(self): 'float': validate_float, 'int': validate_int, 'date': validate_date, + 'datetime': validate_date, 'bool': validate_bool, 'object': validate_object, 'enum': validate_enum, @@ -357,6 +361,7 @@ class Meta: value_float = models.FloatField(blank=True, null=True) value_int = models.IntegerField(blank=True, null=True) value_date = models.DateField(blank=True, null=True) + value_datetime = models.DateTimeField(blank=True, null=True) value_bool = models.NullBooleanField(blank=True, null=True) value_enum = models.ForeignKey(EnumValue, blank=True, null=True, related_name='eav_values') From e606bd79d76f844a3fd390f82cd0a92ddc7820fb Mon Sep 17 00:00:00 2001 From: Thomas Kremmel Date: Mon, 21 Jan 2013 22:45:25 +0100 Subject: [PATCH 17/19] new unique together constraint --- eav/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eav/models.py b/eav/models.py index 51b9ede..a400264 100644 --- a/eav/models.py +++ b/eav/models.py @@ -156,7 +156,7 @@ class Attribute(models.Model): class Meta: ordering = ['name'] - unique_together = ('site', 'slug') + unique_together = ('generic_restriction_id', 'slug') TYPE_TEXT = 'text' TYPE_FLOAT = 'float' From 85eaed2dfe2dba366aa43782cb4619e6cea3a1cc Mon Sep 17 00:00:00 2001 From: Thomas Kremmel Date: Thu, 28 Mar 2013 18:07:38 +0100 Subject: [PATCH 18/19] add localization support --- eav/forms.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/eav/forms.py b/eav/forms.py index b4111aa..207fb18 100644 --- a/eav/forms.py +++ b/eav/forms.py @@ -28,6 +28,7 @@ ''' from copy import deepcopy +from django.conf import settings from django.forms import BooleanField, CharField, DateField, DateTimeField, FloatField, \ IntegerField, ModelForm, ChoiceField, ValidationError from django.contrib.admin.widgets import AdminSplitDateTime @@ -60,6 +61,24 @@ def __init__(self, data=None, *args, **kwargs): config_cls = self.instance._eav_config_cls self.entity = getattr(self.instance, config_cls.eav_attr) self._build_dynamic_fields() + use_l10n = getattr(settings, 'USE_L10N') + if use_l10n == True: + self.localize_fields() + + def localize_fields(self): + """ + set localization to True for all fields + """ + for name, field in self.fields.items(): + widget_type = field.widget.__class__.__name__ + if widget_type != "DateInput" and widget_type != "DateTimeInput" and widget_type != "TimeInput": #do not localize date / time input widgets + field.localize = True + field.widget.is_localized = True + else: + field.localize = False + field.widget.is_localized = False + if widget_type == "DateInput": + field.widget.format=settings.DATE_FORMAT def _build_dynamic_fields(self): # reset form fields @@ -84,7 +103,6 @@ def _build_dynamic_fields(self): defaults.update({'choices': choices}) if value: defaults.update({'initial': value.pk}) - elif datatype == attribute.TYPE_DATE_TIME: defaults.update({'widget': AdminSplitDateTime}) elif datatype == attribute.TYPE_OBJECT: From d845d47c31aa82d8ec6cbec59058469e7bbe22af Mon Sep 17 00:00:00 2001 From: Thomas Kremmel Date: Thu, 28 Mar 2013 18:34:56 +0100 Subject: [PATCH 19/19] clean up --- eav/forms.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/eav/forms.py b/eav/forms.py index 207fb18..cb4dbc7 100644 --- a/eav/forms.py +++ b/eav/forms.py @@ -79,6 +79,10 @@ def localize_fields(self): field.widget.is_localized = False if widget_type == "DateInput": field.widget.format=settings.DATE_FORMAT + if widget_type == "DateTimeInput": + field.widget.format=settings.DATETIME_FORMAT + if widget_type == "TimeInput": + field.widget.format=settings.TIME_FORMAT def _build_dynamic_fields(self): # reset form fields