How to handle non model variable

Follow up question: creating and linking two new models at once

I am trying to create and handle form elements, not created in a model, to define if the user wants to create a second (other) model instance. So far adding the field works but when I try to handle the checkbox and create a model I have not found a way to access the variable. My assumption is I have to handle it in the model class save function?!

I was able to add a checkbox and show it (testing atm so some weird names and colors) with the following code:

forms.py:

class EntityModelForm(ContextModelForm):
    addSomething = forms.BooleanField(
            label = 'test',
            widget = forms.CheckboxInput(
            )
        )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["addSomething"] = forms.BooleanField()
        self.fields["addSomething"].label = 'my fantastic label'
        self.fields["addSomething"].help_text = 'my helptext for the fantastic label'
        self.fields["addSomething"].required = False

    class Meta:
        model = Entity
        fields = "__all__"

the init part based upon: this post

the template part:

{% load i18n %}
<hr>
<div class="data-element-group">
  <label for="{{ form.addSomething.id_for_label }}">{{ form.addSomething.label }}</label>

  {{ form.addSomething }}

  <div class="errors">
    {{ form.addSomething.errors }}
  </div>

  {{ form.addSomething.help_text }}
</div>

And the visual result (with some fantastic css):
result
(sass available on request :wink: )

when pressing save with the models.py code:

    def save(self, *args, **kwargs):
        if self.addSomething:
            pass

resulting in

AttributeError at .....
object has no attribute 'addSomething'

logical because addSomething is only defined in the forms.py

and

        do_it = self.form.addSomething
        print('yes'+str(do_it) )

        return super(Entity, self).save(*args, **kwargs)

resulting in:

  AttributeError ..... object has no attribute 'form'`

logical and when using from .forms import * it was even worse and did not compile.

Does anybody know how to get the variable and see if it is checked so I can handle it to create another (different) model instance

In your view, after the form.is_valid test, you have the cleaned_data dict available on the form object to access the individual fields.

In my createview I added the method:

    def form_valid(self, form):
        print('test: '+ str(form.cleaned_data['addSomething']))
        return super().form_valid(form)

and now I can see the value for addSomething and I can start making decent code. It works thank you.

I expected I had to do something in the save method. You write “after the form.is_valid test”. Did you refer to the form_valid (…) function with the word ‘after’ and if so is there (beside this documentation of form validation tests in the correct order and what to perform where?

@KenWhitesell Thank you for all your responses.

Chronologically, not necessarily physically in the code.

Are you using a Class-Based View? (I’m assuming that’s where this form_valid comes into play.)

If so, then this is the right place to do this. The CBV post method calls form.is_valid, and then dispatches to either form_valid or form_invalid, depending upon the result of the tests. So by referencing cleaned_data in form_valid, this reference is occuring after form.is_valid is called.

yes I am using class based views.

So am I reading this correctly and overwriting the post method in the class based view might give me access to the field too but before validation?

thanks again

Not in cleaned_data, no. It’s the is_valid call that causes that dict to be populated.

If you want to access the field before the form is cleaned, you’d need to either refer directly to the entry in the request.POST object, or the field in the form after it has been bound. (Or, you can override the clean_<fieldname> method itself to get the value as it’s being cleaned.)

thanks … and a few more words to get to 20 :slight_smile:

It will work but I found a problem I did not expect when implementing it. All models extend a baseModel with a

labels = models.ManyToManyField(to='Label', blank=True)

I wanted to give the user an option to add a label on save instead of a creating separate steps (create and then link the label).

I can see the labels with form.cleaned_data[‘subjects’] as a queryset. I can create a new label. Is it possible to add the newly created label either to the queryset or otherwise to the new model from within the CreateView → form_valid method?

ps. do you prefer me to open a new question so I can mark that with the ‘solution marker?’

A many-to-many field is another model containing two foreign keys - one to each of the two related tables. This join table can’t be populated until after the new data exists in both referenced tables.

If you look at what form_valid is doing in a CreateView, it’s only doing two things.

  • form.save
  • return HttpResponseRedirect

What you’re looking to do is change the sequence of events to be either:

  • form.save
  • create many-to-many entries
  • return HRR

or

  • form.save
  • get HRR object
  • create many-to-many entries
  • return HRR object

The latter form really translates to:

  • response = super …
  • create m2m entries
  • return response

Up to you really. Kinda depends how far we’re going to continue to drift from the original topic with future discussions along this line.

I will create a new topic (tomorrow)… and link both questions. I need some time to write it and it might become bigger looking at your answer. I can probably get it working but not in a decent way… one solution → more problems

thanks again