简体   繁体   中英

Django Form Dynamic Fields looping over each field from POST and creating records

I'm looking for some advice where to go from here. I've been working on making a Form, which dynamically generates its fields.

The form is working and generating everything correctly. However, I am having issues with how to save the actual form data. I'm looking for each field to save as a new item in a model.

The View Class from view.py

class MaintenanceCheckListForm(LoginRequiredMixin, FormView):
login_url = '/accounts/login'
template_name = 'maintenance/checklist.html'
form_class = MaintenanceCheckListForm
success_url = reverse_lazy('m-checklist')

def form_valid(self, form):
    form.cleaned_data
    for key, values in form:
            MaintenanceCheckList.objects.create(
                item = key,
                is_compliant = values
        )
    return super().form_valid(form)

The Form from forms.py

class MaintenanceCheckListForm(forms.Form):

def __init__(self, *args, **kwargs):
    super(MaintenanceCheckListForm, self).__init__(*args, **kwargs)
    
    items = Maintenance_Item.objects.all()

    CHOICES = (
        ('P','Compliant'),
        ('F','Non-Compliant'),
    )

    for item in items:
            self.fields[str(item.name)] = forms.ChoiceField(
            label=item.name,
            choices=CHOICES,
            widget=forms.RadioSelect,
            initial='F',
        )

The Model, from models.py

class MaintenanceCheckList(CommonInfo):
CHOICES = (
    ('P','Compliant'),
    ('F','Non-Compliant'),
)

id = models.AutoField(primary_key=True)
item = models.CharField(max_length=100)
is_compliant = models.CharField(max_length=20, choices= CHOICES)

I am having trouble accessing the data from the Form when it POST's. I've done some troubleshooting where I have set the values statically in the '''form_valid''' and it appears to generate the correct amounts of entires in the model. However the trouble begins when I attempt to insert the values from the POST.

I receieve the below error, which I believe it is trying to dump all the keys and values into a single item instead of looping over each key, value and creating the item.

DataError at /maintenance/checklist
value too long for type character varying(100)
Request Method: POST
Request URL:    http://t1.localhost:8000/maintenance/checklist
Django Version: 3.1.6
Exception Type: DataError
Exception Value:    
value too long for type character varying(100)

I'm fairly new to the world of Django (4 weeks and counting so far, and maybe 12 weeks into python). So any assistance would be amazing!

I believe you have somewhat gone on a tangent. There's a simpler solution of using Model formsets for what you want.

First if you want a custom form make that:

from django import forms


class MaintenanceCheckListComplianceForm(forms.ModelForm):
    item = forms.CharField(widget = forms.HiddenInput())
    is_compliant = forms.ChoiceField(
        choices=MaintenanceCheckList.CHOICES,
        widget=forms.RadioSelect,
        initial='F',
    )
    
    class Meta:
        model = MaintenanceCheckList
        fields = ('item', 'is_compliant')

Next use it along with modelformset_factory in your views:

from django.forms import modelformset_factory


class MaintenanceCheckListFormView(LoginRequiredMixin, FormView): # Changed view name was a bit misleading
    login_url = '/accounts/login'
    template_name = 'maintenance/checklist.html'
    success_url = reverse_lazy('m-checklist')
    
    def form_valid(self, form):
        instances = form.save()
        return super().form_valid(form)
    
    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs['queryset'] = MaintenanceCheckList.objects.none()
        kwargs['initial'] = [{'item': obj['name'], 'is_compliant': 'F'} for obj in Maintenance_Item.objects.all().values('name')]
        return kwargs
    
    def get_form(self, form_class=None):
        kwargs = self.get_form_kwargs()
        extra = len(kwargs['initial'])
        form_class = modelformset_factory(MaintenanceCheckList, form=MaintenanceCheckListComplianceForm, extra=extra)
        return form_class(**kwargs)

Now in your template:

<form method="post">
    {{ form }}
</form>

Or manually render it:

<form method="post">
    {{ form.management_form }}
    {% for sub_form in form %}
        Item: {{ sub_form.item.value }}
        {{ sub_form }}
    {% endfor %}
</form>

Note : The above usage is a bit weird due to the naming of the formset variable as form by the FormView you should look into improving that a bit.

Note : Looking at the implementation it feels a bit weird to do this. I would advice you to redesign your models a bit. Perhaps a foreign key between your models? It basically feels like you have duplicate data with this implementation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM