5

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.

Dmckim
  • 63
  • 6
  • I don't really see how the code of *forms.py* can produce the error you show. But you're obviously not showing the exact code you're running since it's full of syntax errors (like missing colons, missing braces and names of classes that would confuse anyone). Please paste the actual code and then we might help to find the error. – dirkgroten Oct 04 '18 at 17:51
  • Also, if you want to build a form based on the contents of a Model field, I assume you mean a particular instance of that Model. So somehow you'd pass an instance to the initializer of your form, why not just create the fields in the Form's `__init__()` method? – dirkgroten Oct 04 '18 at 17:52
  • The reason I'm not using the `__init__()` method is I can't figure out how to do it. Do you have a suggestion I can use? I have tried several different methods and the one above I did from memory since I didn't have the code anymore. I apologize for that and I will edit in a minute with the actual code. – Dmckim Oct 04 '18 at 18:45

1 Answers1

1

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.

jrief
  • 1,516
  • 13
  • 9