Search code examples
pythondjangodjango-formsdjango-jsonfield

Django JsonField to forms


I have searched for a couple days to try to find a DRY way to create dynamic forms from a JsonField using django's built in validation and form rendering. I have yet to find a solution that will incorporate django's validation and rendering so I would like to create a module for this but I am having a hard time figuring out how the classes work.

I have tried a couple different things such as the following:

models.py

from django.contrib.postgres.fields import JSONField
from django.db import models

class Forklift(models.Model):
    name = models.CharField(max_length=50)
    json_fields = JSONField()

forms.py

from django import forms
from .models import Forklift

fields_dict = {
    'name': forms.CharField(max_length=25),
    'number': forms.IntegerField()
}

class ModelForm(forms.ModelForm):
    class Meta:
        model = Forklift
        exclude = ['json_fields']

class DynamicForm(forms.Form):
    pass

This is the error I get:

Error

TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

It looks like forms.Form subclasses forms.BaseForm and forms.DeclaritaveFieldsMetaclass but I can'f figure out how to subclass forms.Form to pass in dynamic fields from a dictionary. I have also tried the following:

views.py

from django import forms
from django.shortcuts import render
from .forms import fields_dict

def dynamic_form(request): #__init_subclass__() takes no keyword arguments
    class NewForm(forms.BaseForm, fields=fields_dict): # also tried with fields=fields_dict
        pass
    form = NewForm(request.POST or None)
    return render(request, 'template.html', {'form': form})

def dynamic_form(request): # form will only render once then disappear
    content = {}
    context = {}

    dynamic_form = type('dynamic_form', (DynamicForm,), fields_dict)
    form = dynamic_form(content)

    context = {
        'form': form,
    }
    return render(request, 'activity/dynamic_form.html', context)


def dynamic_form(request): # module 'django.forms' has no attribute 'DeclaritaveFieldsMetaclass'
    class NewForm(forms.BaseForm, metaclass=DeclarativeFieldsMetaclass(MediaDefiningClass),  data=fields_dict):
        pass
    form = NewForm(request.POST or None)
    return render(request, 'template.html', {'form': form})

I am not just asking f an answer, what I would really like to know is how someone walks through all these classes to figure out how to subclass them. I think I can figure out how to make this work by coding all the logic, validation etc myself but I would like this to be a module that other people can use.

urls.py

from django.urls import path

from .views import dynamic_form, test

urlpatterns = [
    path('form/', dynamic_form),
]

Edit

I originally typed the code from memory and had some typos. I Copied the code from my codebase and included urls.py.


Solution

  • Have a look at this third party package django-entangled, then rewrite your form as

    from entangled.forms import EntangledModelForm
    
    class DynamicForm(EntangledModelForm):
        name = forms.CharField(max_length=25)
        number = forms.IntegerField()
    
        class Meta:
            model = Forklift
            entangled_fields = {'json_fields': ['name', 'number']}
    

    Then render the form as you usually would, when using a standard Django form.