Hi, everyone.
I’m having hard times to implement EAV-like logic.
Context:
I’m having some stock stuff (let’s declare it Item model):
class Item(models.Model):
item_type = models.ForeignKey(ItemType)
name = models.CharField(max_length=128)
attributes = models.JSONField(default=dict)
So I want to attach some attributes (after some research I found JSONField more flxible overall even with some drawbacks in validation).
Goal for implementation:
Allow users to input attributes and their values to form for further data aggregation (Sum/Cont Item availability on stock having provided attributes). The problem that attributes field may (and would) be expected to contain more then one attribute and I’m struggling to implement the form where single JSON field will contain 2+ fields (key/value pairs) or even more as part of formset where each form will have JSON field containing multiple key/value pairs fields.
Hoping someone may guide me on right direction on implementation of such feature or even give me better approach I couldn’t see.
Thank to everyone in advance.
Adding more models in case it helps to build a wider view of models desing:
class AttributesDataTypes(models.TextChoices):
INT = "int", _("Integer")
FLOAT = "float", _("Float")
STR = "str", _("String")
BOOL = "bool", _("Boolean")
LIST = "list", _("List")
class ItemAttribute(models.Model):
name = models.CharField( max_length=100, unique=True)
data_type = models.CharField(choices=AttributesDataTypes.choices )
class ItemAttributeSpec(models.Model):
class Meta:
unique_together = ["item_type", "attribute"]
item_type = models.ForeignKey(ItemType)
attribute = models.ForeignKey(ItemAttribute)
required = models.BooleanField(default=False)
choices = models.JSONField(default=list)
Hello there!
Have you considered using an existent implementation of such functionality?
Jazzband have a package for this, it may suit your needs.
If you want to roll your own EAV implementation, I think that using the existing one as a reference may help you to find a good approach to this problem. The documentation refers a formset, so you might be able to take some guidance from the source code.
Hope that helps.
Hello, @leandrodesouzadev ! Thank you for a reply.
I’m familiar with Django EAV 2 library and I’m going to test it if JSON approach won’t meet my expectatons. So far I need more flexible approach which JSON offers but I will consider EAV as well if will not be able to make my approach work.
Is there any way to nest multiple key/pair forms within single form instance or it’s going to be mess with prefixes?
There you can create a ModelForm and map arbitrary fields onto a single JSONField. That’s probably all you need.
I am currently working on a improved version of this, where you can store the serialized content of complete formsets inside a JSONField. Ping me if you need this.
This may also be relavent to what you are looking for? On that page they at least have an admin widget that is capeable of using multiple HTML inputs to feed a single JSON field. I haven’t used it yet but am investigating the possiblities for one of my own projects.
Thanks everyone for a response helping to find a solution.
I managed to make it work in some way:
I have created a common Form to handle attributes field which takes json_schema (list of objects) on __init__ which contains {"name": "field_name", "data_type": "int", "field_kwargs"} an dynamically generates fields.
Then I’ve created core ModelForm which inherits Form described above and passes it all necessary data builded with ItemAttributeSpec fields data (e.g required , choices, attribute__name ).
And finally I made a view which recieves AJAX/HTMX call on item_type change event and returns forms with related attributes.
Not an ideal solution so far as some fields may required customization in future but works as expected in my particular scenation.
Field type generated as per ItemAttributedata_type so less validation requires on form submit.
Will modify the code as per requirements.
Good to know that you’ve find an answer, if you don’t mind, it would be kind to share a “minimal” solution here, so when others in the future search for this topic, they can find a solution that may help them.
Cheers.