I'm working on a Django project and facing difficulties while trying to create a multi-value form field called ProductPropertyField. My goal is to include multiple instances of ProductPropertyField within the final ProductPropertyField, but the rendering doesn't behave as expected.
I have a specific structure I want to achieve:
ProductPropertyField should contain multiple instances of ProductPropertyField. Each ProductPropertyField instance has three fields: key, value, and new_property. I'd like to use the provided code snippet as a reference for my implementation. Here's the code I'm using for ProductPropertyField:
class ProductPropertyWidget(forms.MultiWidget):
use_fieldset = True
def __init__(self, attrs={}, choices=[], key=None):
key_widget = forms.TextInput(attrs=attrs.pop("key_widget", {}))
value_widget = forms.Select(
attrs=attrs.pop("value_widget", {}), choices=choices
)
new_property_widget = forms.TextInput(
attrs=attrs.pop("new_property_widget", {})
)
widgets = [
key_widget,
value_widget,
new_property_widget,
]
super().__init__(widgets, attrs)
def decompress(self, value):
if value:
key, value, new_property = value.split(",")
return [key, value, new_property]
return [None, None, None]
class ProductPropertiesWidget(forms.MultiWidget):
use_fieldset = True
def __init__(self, widgets=[], attrs={}):
super().__init__(widgets=widgets, attrs=attrs)
def decompress(self, value):
if value:
key, value, new_property = value.split(",")
return [key, value, new_property]
return [None for _ in self.widgets]
class ProductPropertyField(forms.MultiValueField):
widget = ProductPropertyWidget
def __init__(self, *args, **kwargs):
key_field = forms.CharField()
value_field = forms.ChoiceField(choices=kwargs.pop("choices", []))
new_property_field = forms.CharField()
super().__init__(
fields=[key_field, value_field, new_property_field],
require_all_fields=False,
*args,
**kwargs,
)
def compress(self, data_list):
return ",".join(data_list)
class ProductPropertiesField(forms.MultiValueField):
def __init__(self, \*args, \*\*kwargs):
widgets = \[\]
fields = \[\]
key_value = ProductProperty.objects.values("feature__key", "feature__value")
values = {}
for property in key_value:
key = values.get(property["feature__key"], None)
if not key:
values[property["feature__key"]] = set()
values[property["feature__key"]].add(property["feature__value"])
for (
key,
values_set,
) in values.items():
value_choices = [(v, v) for v in sorted(values_set)]
widget = ProductPropertyWidget(
attrs={"placeholder": "Some placeholder"},
choices=value_choices,
key=key,
)
field = ProductPropertyField(label=key, choices=value_choices)
fields.append(field)
widgets.append(widget)
fields.append(
forms.CharField(
help_text="New property should be like property_key=value",
label="New property",
)
)
widgets.append(forms.TextInput(attrs={"placeholder": "Add new property"}))
widget = ProductPropertiesWidget(widgets=widgets)
super().__init__(fields=fields, widget=widget, *args, **kwargs)
def compress(self, data_list):
return ",".join(data_list)
I've followed the instructions given in the code comments, but the rendering is not as expected. The nested ProductPropertyField instances don't display correctly, and I'm having trouble figuring out the issue.
Could someone please help me identify the problem and provide guidance on how to correctly render nested ProductPropertyField instances within the final ProductPropertyField?
Thank you for your assistance
HTML Output:
<fieldset>
<legend>Properties:</legend>
<input name="properties_0_0" required="" type="text" />
<select name="properties_0_1" required="">
<option value="brass">brass</option>
<option value="bronze">bronze</option>
<option value="clay">clay</option>
<option value="elephant skin">elephant skin</option>
<option value="gold">gold</option>
<option value="marble">marble</option>
<option value="plastic">plastic</option>
<option value="resin">resin</option>
<option value="vinyl">vinyl</option>
<option value="wooden">wooden</option>
</select>
<input name="properties_0_2" required="" type="text" />
<input name="properties_1_0" required="" type="text" />
<select name="properties_1_1" required="">
<option value="black">black</option>
<option value="blue">blue</option>
<option value="bronze">bronze</option>
<option value="golden">golden</option>
<option value="green">green</option>
<option value="multicolored">multicolored</option>
<option value="off white">off white</option>
<option value="red">red</option>
<option value="steel colored">steel colored</option>
<option value="wooden">wooden</option>
</select>
<input name="properties_1_2" required="" type="text" />
<input name="properties_2" required="" type="text" placeholder="Add new property" />
</fieldset>
[This is the output that I got.](https://i.stack.imgur.com/xpeLU.png)
I want to wrap each instance in seperate fieldset.