How to Fix Issues When Passing collection_type in Django Form __init__

I recently ran into a frustrating error while working on a Django project. I was overriding the __init__ method of a ModelForm to customize form behavior based on a collection_type, and Django threw this lovely gem at me:

TypeError: __init__() got multiple values for keyword argument 'collection_type'

At first glance, it didn’t make any sense. I passed all arguments correctly (or so I thought), and the error message wasn’t pointing to anything obviously wrong. After banging my head against the wall for an hour or so, I finally figured it out.

Let me walk you through what caused this error, how I fixed it, and how I made my form even smarter afterward. Hopefully, this saves you the time I lost.

My Code

Here’s the form I initially wrote:

class CreateCollectionForm(forms.ModelForm):
def __init__(self, collection_type, user=None, parent=None, *args, **kwargs):
self.collection_type = collection_type
if collection_type == 'library':
self.user = user
elif collection_type in ['bookshelf', 'series']:
self.parent = parent
else:
raise AssertionError('collection_type must be "library", "bookshelf" or "series"')

super(self.__class__, self).__init__(*args, **kwargs)

And I instantiated the form like this:

form = CreateCollectionForm(
request.POST,
collection_type=collection_type,
parent=parent,
user=request.user
)

Why This Broke

When I passed request.POST as the first positional argument, Python assumed it was meant for the first declared parameter of __init__, which was collection_type.

But I also passed collection_type=collection_type explicitly as a keyword argument. So essentially, Python saw two values for collection_type one from the positional request.POST and one from the keyword and panicked.

Here’s the rule Python follows:
You cannot pass a single argument as both a positional and a keyword argument at the same time.

CreateCollectionForm(request.POST, collection_type=collection_type)

caused the TypeError.

The Correct Code

I changed the constructor to follow Django best practices by allowing *args, **kwargs first, and then extracted the custom arguments manually:

class CreateCollectionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
collection_type = kwargs.pop('collection_type', None)
user = kwargs.pop('user', None)
parent = kwargs.pop('parent', None)

self.collection_type = collection_type
self.user = user
self.parent = parent

if collection_type not in ['library', 'bookshelf', 'series']:
raise AssertionError('collection_type must be "library", "bookshelf" or "series"')

super(CreateCollectionForm, self).__init__(*args, **kwargs)

And now, I could safely call the form like this:

form = CreateCollectionForm(
request.POST,
collection_type=collection_type,
parent=parent,
user=request.user
)

No more TypeError.

Making the Form Smarter

Once the error was fixed, I decided to go a step further. I wanted the form to behave differently depending on the collection_type for example, to change field labels or make certain fields required.

So I added this logic:

class CreateCollectionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
collection_type = kwargs.pop('collection_type', None)
user = kwargs.pop('user', None)
parent = kwargs.pop('parent', None)

self.collection_type = collection_type
self.user = user
self.parent = parent

super(CreateCollectionForm, self).__init__(*args, **kwargs)

if collection_type == 'library':
self.fields['name'].label = "Library Name"
self.fields['description'].required = True
elif collection_type == 'bookshelf':
self.fields['name'].label = "Bookshelf Name"
elif collection_type == 'series':
self.fields['name'].label = "Series Title"
else:
raise AssertionError('collection_type must be "library", "bookshelf", or "series"')

This gave my form a more personalized UX depending on what the user was creating a library, bookshelf, or series.

Final Thought

This little journey reminded me of two important lessons. First, Django forms are incredibly powerful, but their initialization must be handled carefully. When you override the constructor, always start with *args and **kwargs, then extract any custom arguments manually to avoid conflicts. Second, form customization goes far beyond just defining fields in your model. By adjusting the __init__ method, you gain the flexibility to dynamically update field labels, apply conditional validation, or even show or hide fields based on the context — making your forms smarter and more user-friendly.

Related blog posts