creating and linking two new models at once

This is a follow up question after: How to handle non model variable

So I have a baseModel:

class BaseModel(models.Model):
    creation_date = models.DateTimeField(auto_now_add=True)
    last_edited_date = models.DateTimeField(auto_now=True)
    labels = models.ManyToManyField(to='Label', blank=True)

with Label model just a charfield

And a (few) child models extending this. Example:

class Person(BaseModel):
    name = models.CharField(max_length=200)

When I create a new person I can add (check from a list) excisting labels from a list and thanks to this I can now create a new label when creating a new person.

Of course BaseModel is extended so it is created anyway and with previous question I created the Label instance in the

def form_valid(self, form):
     see previous question

Is there a way to link the new label to the labels for the new person instance?

@KenWhitesell I struggle to understand your last answer at this so I dont know if that is the way to go.

My apologies, I really didn’t intend to be cryptic. Please allow me to try and explain.

The CreateView.form_valid method comes from the ModelFormMixin class, which looks like this:

    def form_valid(self, form):
        """If the form is valid, save the associated model."""
        self.object = form.save()
        return super().form_valid(form)

The call to super().form_valid(form) calls FormMixin.form_valid, which looks like this:

    def form_valid(self, form):
        """If the form is valid, redirect to the supplied URL."""
        return HttpResponseRedirect(self.get_success_url())

If we combine the functionality of these two methods into one sequence of code, you get:

    def form_valid(self, form):
        self.object = form.save()
        return HttpResponseRedirect(self.get_success_url())

This is what effectively gets executed when you call super().form_valid(form) in your form_valid method. This means that you could copy these lines into your form_valid, such that it looks like this:

    def form_valid(self, form):
        print('test: '+ str(form.cleaned_data['addSomething']))
        self.object = form.save()
        return HttpResponseRedirect(self.get_success_url())

Why might you want to do this?

Because you need to do something between the self.object = form.save() and the return statement, and that “something” is to create the associations between the Person and Label instances.

Having said that, if your coding standards require you to call super so as to avoid theoretical problems potentially happening in the future if these CBVs were to fundamentally change, you do have an alternative.

Again, if you look at what’s happening with super().form_valid(form), you’re calling a function, and it’s returning a variable to you. You want to return that variable from your function, but that doesn’t mean you need to do it immediately or at the point where it’s being called.

It’s perfectly valid to write a form_valid method that looks like this:

    def form_valid(self, form):
        print('test: '+ str(form.cleaned_data['addSomething']))
        response = super().form_valid(form)
        # Do stuff, like create links between `People` and `Label`
       return response

No apologies needed. I actually love being pointed in the right way and find the rest out myself… and you do a lot here. I wish I was able to.

I will try to solve my problem now

Like a good programmer I made a mess to try it all and here is the mess: :grinning:

In my class PersonCreateView(CreateView): (some basic code removed)
I added:

    def form_valid(self, form):
        print('name: '+ form.cleaned_data['name'])
        print('add checked: ' + str(form.cleaned_data['addSomething']))
        print('labels: ' + str(form.cleaned_data['labels']))

this printed the person name, the addSomething status from previous question and the optional labels from the list of excisting labels

Next I created a new label (adding 10+ test labels in my database in the process)

        testLabel = Label(name='test')
        testLabel.save()
        print('test: ' + str(testLabel) )

Next I called:

        self.object = form.save()
        print('object: ' + str(self.object))

        response = super().form_valid(form)

saving the new person and printing the name again, preparing the response object and last:

        self.object.labels.add(testLabel)
        self.object.save()
        print('labels: ' + str(self.object.labels ))

        print(response)

        return response

For others that try this too: I tried to add the new label before the call to self.object = form.save() but that failed.

Like a rare programmer I will now try to clean up the mess, make it work and look pretty :wink: …maybe… one day.

Side note:

The self.object.save() at this point is unnecessary.

Adding references in a ManyToMany relationship does not affect either of the two models involved. The only model being affected is the “through” or “join” model (that’s two different names for the same thing, I’m not referring to two different models).

Since self.object is not affected by the self.object.labels.add(...) statement, there’s no need to save it again.