Thank you for your reply, @KenWhitesell.
I’m attaching partial source code of class ProductAdminForm and class BaseDynamicEntityForm. Could you please see if it helps?
By the way, I suspect the call of super(BaseDynamicEntityForm, self).__init__(data, *args, **kwargs) has a synchronization issue: if I set a debug break point right behind it, the error will not happen; if removed the break point, the error happens. Please let me know if you want to test in this area.
If necessary, could you please give me an example of clean_ method?
Thank you for your patience and help, and for anything just let me know.
class ProductAdminForm(BaseDynamicEntityForm):
"""
This is an admin form which uses the EAV class
"""
model = models.Product
def _build_dynamic_fields(self):
"""
this is an integration point, overrides existing method in third party source
:return:
"""
# reset form fields
print("admin _build_dynamic_fields: %s" % self.instance.product_template)
self.fields = deepcopy(self.base_fields)
#product_template = self.initial.get('product_template') or self.instance.product_template
#
option_1 = self.initial.get('product_template')
option_2 = self.instance.product_template
product_template = option_1 or option_2
all_attributes = self.entity.get_all_attributes()
the_attributes = all_attributes.filter(producttemplateattribute__product_template=product_template)
# this line is the primary reason for override, additional filter on the fields by business
#for attribute in self.entity.get_all_attributes().filter(producttemplateattribute__product_template=product_template):
#
for attribute in the_attributes:
value = getattr(self.entity, attribute.slug)
defaults = {
'label': attribute.name.capitalize(),
'required': attribute.required,
'help_text': attribute.help_text,
'validators': attribute.get_validators(),
}
datatype = attribute.datatype
if datatype == attribute.TYPE_ENUM:
enums = attribute.get_choices() \
.values_list('id', 'value')
choices = [('', '-----')] + list(enums)
defaults.update({'choices': choices})
if value:
defaults.update({'initial': value.pk})
elif datatype == attribute.TYPE_DATE:
defaults.update({'widget': AdminSplitDateTime})
elif datatype == attribute.TYPE_OBJECT:
continue
MappedField = self.FIELD_CLASSES[datatype]
self.fields[attribute.slug] = MappedField(**defaults)
# fill initial data (if attribute was already defined)
if value and not datatype == attribute.TYPE_ENUM: #enum done above
self.initial[attribute.slug] = value
return
class BaseDynamicEntityForm(ModelForm):
'''
ModelForm for 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).
'''
FIELD_CLASSES = {
'text': CharField,
'float': FloatField,
'int': IntegerField,
'date': DateTimeField,
'bool': BooleanField,
'enum': ChoiceField,
}
def __init__(self, data=None, *args, **kwargs):
print("**kwargs: [[ ")
for key, value in kwargs.items():
print("%s == %s" % (key, value))
print("]]")
super(BaseDynamicEntityForm, self).__init__(data, *args, **kwargs)
print("self.instance: %s" % self.instance)
print("-------------------------")
config_cls = self.instance._eav_config_cls
self.entity = getattr(self.instance, config_cls.eav_attr)
self._build_dynamic_fields()