dJudge
dJudge

Reputation: 186

Error in as_crispy_field got passed an invalid or inexistent field

The page returns the error as_crispy_field got passed an invalid or inexistent field after SUBMIT Button is clicked. I was trying to submit the form when the error is raised. The record is SUCCESSFULLY saved into the database but an error is raised. A form for the template was created to set up the fields. The Form was instantiated in the views so that it could easily map the elements from the FORM to the TEMPLATE.

What caused the error and how can I resolve it?

FORM: Here is my form code

class ModelCreateDescriptionForm(forms.ModelForm):
    name = forms.CharField(max_length=100)

    description = forms.Textarea()

    model_type = forms.ChoiceField(
        widget = forms.Select,
        choices = MODEL_TYPE_CHOICES,
    )

    def __init__(self, project_id=1, *args, **kwargs):
        super(ModelCreateDescriptionForm, self).__init__(*args, **kwargs)
        
        # project = Project.objects.get(id=project_id)

        self.fields['model_type'].choices = MODEL_TYPE_CHOICES
        self.fields['model_type'].required = True

    class Meta:
        model = Model    
        fields = ['name', 'description', 'model_type']

VIEW: Here is my view

def modelCreationDescription(request, **kwargs):
    pk = kwargs.get('pk')
    project = Project.objects.get(id=pk)

    context = {}
    context['project'] = project

    if request.method == 'POST':
        form = ModelCreateDescriptionForm(pk, request.POST)

        name = request.POST.get(u'name')
        description = request.POST.get(u'description')
        model_type = request.POST.get(u'model_type')

        if not(name and model_type):
            messages.warning(request, f'Please fill model name and model type!')

        if form.is_valid(): 
            formDescription = form.save(commit=False)

            try:
                formDescription.name = name
                formDescription.description = description
                formDescription.model_type = model_type
                formDescription.project = project
            except:
                messages.warning(request, f'Something wrong!')
                return redirect('all-model-listview')


            # save the form value
            formDescription.save()

            messages.success(request, f'Model description successfully created')

            return render(request, 'models/pred_steps/modelSetTargetFeatures.html', {'model_form': formDescription })
    else:
        form = ModelCreateDescriptionForm(project_id=pk)

    context = {
        'form': form,
        'project': project,
        'create_model_description': True,
    }

    return render(request, 'models/pred_steps/modelCreateDescriptions.html', context)

HTML: This is the template that returning an error

<div class="card border-left-info  mb-1 shadow">
    <div class="col-xl-10 col-md-8 mb-1">
        {% if project %}
            <p><h5 class="text-info">Project: {{ project }}</h5></p>
            
        {% endif %}

        <form method="POST">
            {% csrf_token %}

            <fieldset class='form-group'>
                {{ form.name|as_crispy_field }}
            </fieldset>

            <fieldset class='form-group'>
                {{ form.description|as_crispy_field }}
            </fieldset>

            <fieldset class='form-group'>
                {{ form.model_type|as_crispy_field }}
            </fieldset>

            <div class="form-group">
                {% if project.id %}
                    <a class="btn btn-outline-secondary float-right" href="{% url 'all-model-listview' %}">Cancel</a>
                {% endif %}
                
                <button class="btn btn-outline-success" type="submit">Submit and Continue</button>
            </div>
        </form>
    </div>
</div>

In case the next template is causing the error here are the codes FORM:

class ModelSetTargetFeaturesForm(forms.ModelForm):
    target_column_classification = forms.ChoiceField(
        widget = forms.Select,
    )
    
    target_column_regression = forms.ChoiceField(
        widget = forms.Select,
    )

    def __init__(self, project_id=1, *args, **kwargs):
        super(ModelSetTargetFeaturesForm, self).__init__(*args, **kwargs)
        
        project = Project.objects.get(id=project_id)

        df = pd.read_csv(project.base_file, encoding='ISO-8859-1')
        cols = df.columns
        a_cols = np.column_stack(([cols, cols]))

        self.fields['target_column_classification'].choices = a_cols
        self.fields['target_column_classification'].required=True
        
        self.fields['target_column_regression'].choices = a_cols
        self.fields['target_column_regression'].required=True

        # project = Project.objects.get(id=project_id)


    class Meta:
        model = Model    
        fields = ['target_column_classification', 'target_column_regression', ]

VIEW:

def modelSetTargetFeatures(request, **kwargs):
    pk = kwargs.get('pk')
    model = Model.objects.get(id=pk)
    project = Model.objects.get(project=model.project.pk)

    context = {}
    context['model'] = model
    context['project'] = project

    if request.method == 'POST':
        form = ModelSetTargetFeaturesForm(pk,request.POST)

        target_column_classification = request.POST.get(u'target_column_classification')
        target_column_regression = request.POST.get(u'target_column_regression')


        if not(target_column_regression and target_column_classification):
            messages.warning(request, f'Please fill model name and model type!')

        if form.is_valid():
            formTargetFeatures = form.save(commit=False)

        formTargetFeatures.target_column_classification = target_column_classification
        formTargetFeatures.target_column_regression = target_column_regression

        # save the form value
        formTargetFeatures.save()

        messages.success(request, f'Model description successfully created')

        return render(request, 'models/pred_steps/modelFeaturesSelection.html', {'model_form': formTargetFeatures })
    else:
        form = ModelSetTargetFeaturesForm(model=pk)

    context = {
        'form': form,
        'project': project,
        'model': model,
        'create_model_description': True,
    }

    return render(request, 'models/pred_steps/modelSetTargetFeatures.html', context)

TEMPLATE:

<div class="card border-left-info  mb-1 shadow">
    <div class="col-xl-10 col-md-8 mb-1">
        {% if project %}
            <p><h5 class="text-info">Project: {{ project }}</h5></p>
            <p><h5 class="text-info">Model: {{ name }}</h5></p>  
        {% endif %}

        <form method="POST">
            {% csrf_token %}

            <fieldset class='form-group'>
                {{ form.target_column_classification|as_crispy_field }}
            </fieldset>

            <fieldset class='form-group'>
                {{ form.target_column_regression|as_crispy_field }}
            </fieldset>

            <div class="form-group">
                {% if project.id %}
                    <a class="btn btn-outline-secondary float-right" href="{% url 'all-model-listview' %}">Cancel</a>
                {% endif %}
                
                <button class="btn btn-outline-success" type="submit">Next: Feature Selection</button>
            </div>
        </form>
    </div>
</div>

Upvotes: 1

Views: 1757

Answers (1)

datalowe
datalowe

Reputation: 599

Your view is making use of two different html templates:

'models/pred_steps/modelCreateDescriptions.html'

and

'models/pred_steps/modelSetTargetFeatures.html'

The first one is used for presenting the form, upon a GET request, and allowing the participant to input their data: return render(request, 'models/pred_steps/modelCreateDescriptions.html', context)

However, once the participant's data are POST'ed, this happens:

form = ModelCreateDescriptionForm(pk, request.POST)
# ... inside of if_valid
    formDescription = form.save(commit=False)
    # ...
    formDescription.save()
    # ...
    return render(request, 'models/pred_steps/modelSetTargetFeatures.html', {'form': formDescription })

It's this second rendition that is causing problems, so I assume you are using crispy fields in the modelSetTargetFeatures.html template also. When rendering this other template, you seem to try to include formDescription as a form. However, formDescription is not a form, because form.save(commit=False) returns a Model object, not a form. Hence, django/crispy forms doesn't understand what's going on and rightly says that what you are trying to use as a form is not a valid form (since it's actually a Model instance). If you really do want to pass the form itself on to the other template, you can simply use {'form': form}.

You might also want to use a better name for your model than simply Model. It's very confusing and might cause bugs, since the name is identical to that of django.db.models.Model, which we subclass from for creating specific Models. Additionally, you might want to use the pythonic naming convention of using snakecase (e. g. my_function_based_view) for functions, and camelcase with the first letter capitalized for classes (e. g. MyFormClass).

(note that the above response refers to the code as you originally posted it - you've done some edits since I started responding, but the basic issue seems to be the same even after the edits)

Upvotes: 1

Related Questions