Search code examples
pythonpython-3.xdjangodjango-formsdjango-widget

Django Error: TypeError: __init__() got an unexpected keyword argument 'attrs'


I'm trying to create a form where I'm getting certain details from the user.

I have defined fields in forms.py and I'm also defining other attributes like placeholder and css class using django widget system. But it is showing me a TypeError:

TypeError: __init__() got an unexpected keyword argument 'attrs'

Following is my code:

models.py

from django.db import models

class Contact(models.Model):
    your_name = models.CharField(max_length=100) 
    your_email = models.EmailField()
    your_subject = models.CharField(max_length=100)
    your_comment = models.TextField(max_length=200)
 
 def __str__(self):
    return self.name

forms.py

from django.forms import ModelForm, TextInput, TextInput, EmailField
from .models import Contact

class ContactForm(ModelForm):
    class Meta:
        model = Contact
        fields = ('your_name', 'your_email', 'your_subject', 'your_comment')
        widgets = {
            'your_name' : TextInput(attrs={'placeholder': 'Name *', 'class': 'form-control'}),
            'your_email' : EmailField(attrs={'placeholder': 'Email *', 'class': 'form-control'}),
            'your_subject' : TextInput(attrs={'placeholder': 'Subject *', 'class': 'form-control'}),
            'your_comment' : Textarea(attrs={'placeholder': 'Comment *', 'class': 'form-control'}),
        }

I've read Django docs for Overriding default fields and also this question init() got an unexpected keyword argument 'attrs' but cannot fix the error.

I am quite new to Python and Django and would appreciate any help, thank you.


Solution

  • TL;DR: EmailField is a just that a Field which can't be set as a widget, if you tried you'd run into the below problem, you want EmailInput which is the widget for EmailField.

    First, TextInput and Textarea accept the keyword argument attrs in their __init__. So, these are fine.

    Look at this line:

    EmailField(attrs={'placeholder': 'Email *', 'class': 'form-control'}),
    

    EmailField is a subclass of CharField which in turn is a subclass of Field. Throughout this hierarchy attrs will be passed along until it hits Field which doesn't accept attrs.

    For reference here's the __init__ for Field:

      def __init__(self, required=True, widget=None, label=None, initial=None,
                   help_text='', error_messages=None, show_hidden_initial=False,
                   validators=(), localize=False, disabled=False, label_suffix=None):
    

    The __init__ for CharField:

     def __init__(self, max_length=None, min_length=None, strip=True, empty_value='', *args, **kwargs)
    
          ....
          super(CharField, self).__init__(*args, **kwargs)
          # attrs is passed to Field -> error
    

    The __init__ for EmailField:

     def __init__(self, *args, **kwargs):
        super(EmailField, self).__init__(*args, strip=True, **kwargs)
        # attrs is passed to CharField
    

    There is a widget argument that is accepted in which you can place a widget that does accept the attrs keyword.