Set default values in a CreateView

Hi all,

I want to add an entry to a Model by using a CreateView, but would like to specify one single formfield by the class based view and not by the user.

Here is my approach where the user specifies the field “my_foreign_key” which works fine. (I simplified the code for making it easier to read.)

MyModel:

class MyModel(models.Model):
    field1 = models.CharField('Field1', max_length=200, unique=True)
    field2 = models.CharField('Field2', max_length=200, unique=True)
    my_foreign_key = models.ForeignKey(SomeOtherModel, on_delete=models.CASCADE)   

CreateView:

class CreateCampaign(generic.CreateView):
    model = MyModel
    template_name = "...PATH..."
    form_class = MyModelForm
    success_url = '...PATH...'

MyFormView

class MyModelForm(ModelForm):

    class Meta:
        model = MyModel
        fields = ["field1", "field2", "my_foreign_key"]
	
	@property
	def helper(self):
		helper = FormHelper()
        helper.layout = Layout(
		Field('field1'),
		Field('field2'),
		Field('my_foreign_key'),
		)
		return helper

This works fine. But now I would like to set the “my_foreign_key” formfield by the CreateView, for putting in a variable instead of some user input. Reading through the documentation, this thread[1] and the specifications of the CreateView[2], I assume that the “get_form_kwargs” function is what I am looking for.

But changing the code doesn’t really work for me:
The updated CreateView:

CreateView:

class CreateCampaign(generic.CreateView):
    model = MyModel
    template_name = '...PATH...'
    form_class = MyModelForm
    success_url = '...PATH...'

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs['my_foreign_key'] = 42
        return kwargs


    class Meta:
        model = MyModel
        fields = ["field1", "field2", "my_foreign_key"]

Updated MyFormView

class MyModelForm(ModelForm):
    def __init__(self, *args, **kwargs):
        self.my_foreign_key = kwargs.pop('my_foreign_key', None)
        super().__init__(*args, **kwargs)
...

When I delete the “my_foreign_key” formfield from MyModelForm, I receive the following error:

django.db.utils.IntegrityError: NOT NULL constraint failed: myapp.my_foreign_key

Playing around for ~2 hours, I either get this error, or, if I don’t delete the “my_foreign_key” from the “fields” list in “class Meta”, I receive an invalid form.

So I’m interesed if my approach is going into the right direction.

BR

[1] altering queryset in a field of generic CreateView form - #5 by KenWhitesell
[2] CreateView -- Classy CBV

My first question is, where is the value for that foreign key going to come from?

Is it always going to be 42?

Bypassing that question for the moment, if you look through what’s happening in a CreateView, you will find that the method you want to override is form_valid.
(Note, in addition to the docs, I always recommend the Classy Class Based View site along with the CBV Diagrams pages for getting an understanding of how these work.)

From what I can tell from your description, you’re not looking to change the form. It appears to me that you want to set a value in a Model that is not supplied on the form - a completely different issue. The reference to the earlier post doesn’t apply here.

Thanks (again) KenWhitesell for your clarification and reply on a short notice. :grinning:

I thought about using the form_valid mehtod before and looked again into the Classy Class Based View site and the CBV Diagrams pages. I’m sure it’s me, but I just don’t get it: How can I add a key-value pair to the form there? From my google searches, I get hints for making some of those miserable attempts which (obviously) don’t work…

    def form_valid(self, form):
        form.cleaned_data['my_foreign_key'] = 42
        self.object = form.save()
        return super().form_valid(form)

or

    def form_valid(self, form):
        listh = RecipientLists.objects.get(pk=42)
        self.object['my_foreign_key'] = 42
        self.object = form.save()
        return super().form_valid(form)

So how can I add a key/value pair in the form_valid method?

===
P.S. The 42 later shall be the PK the url is called with, e.g. 127.0.0.1:8000/myapp/create/42
Why? Because the process looks like that:

  1. Upload an Excel-Form which opened by pandas and later saved in a model
  2. Redirect to 127.0.0.1:8000/myapp/create/[pk of the db entry, e.g. 42]
  3. Create another entry which is stored in another model. The pk of the db entry is stored as foreign key in the model.
    If you see a better way for solving this or are interested in any details, please let me know. :slight_smile:

You don’t want to change the form. (At least I’m not seeing anything from what you’ve written that you want to change the form.) This data that you want to supply has nothing to do with the form. This is additional data you wish to use in your model.

You’re really close here. On a create, self.object doesn’t yet exist. The only data is in the form. Also, model fields are attributes of the object, not elements within a dict.

You have two basic options:

  • Take advantage of the form.instance attribute:
form.instance.my_foreign_key = 42
super().form_valid(form)

(Note: You do not need to call save in your instance of form_valid. The form_valid method being called via super will save the form.)

  • Create the object, modify the it before commit, and redirect (without calling super):
new_object = form.save(commit=False)
new_object.my_foreign_key = 42
new_object.save()
return redirect...

(Completely bypassing the super-classes)

Thanks a lot @KenWhitesell . Again you solved something I tried to solve for hours. So good, to have you here!

Just some side-notes:
Both code examples raised the following error:

ValueError: Cannot assign "42": "MyModel.my_foreign_key" must be a "MyOtherModel" instance.

But I solved that by using the following code:

    def form_valid(self, form):
        handler = MyOtherModel.objects.get(pk=42)
        form.instance.my_foreign_key = handler
        return super().form_valid(form)

If anyone faces any similar issue: Documentation on form.save() and form instance can be found here.

You don’t want to change the form . (At least I’m not seeing anything from what you’ve written that you want to change the form.) This data that you want to supply has nothing to do with the form . This is additional data you wish to use in your model .

That’s of course correct. I find it a bit difficult to explains those “complex” issues, due to the fact that english is not my native tongue.

Thanks and BR

You can save the db query by using:

def form_valid(self, form):
        form.instance.my_foreign_key_id = 42
        return super().form_valid(form)

Works great, thanks!