2

I am trying to initialize a form containing a ChoiceField in django. I have the following code:

# in file models.py
class Locality(models.Model):
    locality = models.CharField(primary_key=True, unique=True, max_length=36)
    def __unicode__(self):
        return self.locality

# in file forms.py
class RegisterForm(forms.Form): 
    def __init__(self, *args, **kwargs):
        self.username = forms.CharField(required=True)
        self.email = forms.EmailField(required=True)
        self.locality = forms.ChoiceField(widget=forms.Select())
        self.fields['locality'].choices = [l.locality for l in Locality.objects.all()]

but oon the shell, once I try to instanciate:

r = RegisterForm(username="toto", email="a@b.com")

I receive 'RegisterForm' object has no attribute 'fields' error. Is this happening as the object is not already formed? How can I access to the ChoiceField?

Any help appreciated.

Maxime Lorant
  • 34,607
  • 19
  • 87
  • 97
Juan Chô
  • 542
  • 6
  • 23

2 Answers2

7

You're not using the Form object in the good way. The fields attribute is initialize by the __init__ method of BaseForm (see the source code) (a parent class of forms.Form), but you have redefine it, so you broke the process.

So, you should call the parent __init__ in your __init__ method, something like this:

class RegisterForm(forms.Form): 
    username = forms.CharField(required=True)
    email = forms.EmailField(required=True)
    locality = forms.ChoiceField(widget=forms.Select())

    def __init__(self, *args, **kwargs):
         super(forms.Form, self).__init__(*args, **kwargs)
         self.fields['locality'].choices = [(l.id, l.locality) for l in Locality.objects.all()]

I have moved every *Field declaration outside the __init__, because it's the common way. It's question is very similar to a previous one: Override defaults attributes of a Django form

Community
  • 1
  • 1
Maxime Lorant
  • 34,607
  • 19
  • 87
  • 97
  • Updated, just before I see your comment – Maxime Lorant Aug 30 '13 at 11:52
  • `self.fields['locality'].choices = [l.locality for l in Locality.objects.all()]` is wrong. you must use `self.fields['locality'].choices = [(l.id,l.locality) for l in Locality.objects.all()]` i mean choices must be in format `[(..,..),(..,..),..]` – suhailvs Aug 30 '13 at 12:01
  • You're right, I just copy the line of the author without checking the list comprehension, but the idea is here ;) (Updated again) – Maxime Lorant Aug 30 '13 at 12:02
  • Hello Maxime, thanks for your answer, it sound logical. However forms.Form.__init__ is not a recognized symbol and using the __init__ in BaseForm: – Juan Chô Aug 30 '13 at 12:10
  • I have changed my answer with the `super` syntax some minutes ago ;) – Maxime Lorant Aug 30 '13 at 12:10
  • Sorry the text was cut again. Hello Maxime, thanks for your answer, it sound logical. However using your code for instantiating an object r = RegisterForm(username="toto", email="a@b.com") I got an error __init__() got an unexpected keyword argument 'username' – Juan Chô Aug 30 '13 at 12:21
  • You should do this: `r = RegisterForm(initial={"username": "toto", "email":"a@b.com"})`. See the doc: https://docs.djangoproject.com/en/dev/ref/forms/api/#dynamic-initial-values – Maxime Lorant Aug 30 '13 at 12:28
2

try:

def __init__(self, *args, **kwargs):
     super(forms.Form, self).__init__(*args, **kwargs)
     self.fields['locality'].choices = [(l.id, l.locality) for l in Locality.objects.all()]
suhailvs
  • 20,182
  • 14
  • 100
  • 98
  • The list will be created at the declaration of the Form. If he adds a new value in Locality after the server has been launched, this new value will not be in the choices list. Instead, the `__init__` function is called every time you instanciate a new form. – Maxime Lorant Aug 30 '13 at 11:51
  • @MaximeLorant choices must be in format `[(..,..),(..,..),..]` – suhailvs Aug 30 '13 at 12:02
  • @MaximeLorant I am using this methodology in all my projects and it works fine. – suhailvs Aug 30 '13 at 12:02
  • Even if you modify/remove data in `Locality`? Maybe you're right, by looking the class quickly, I see some deepcopy occured... Get some doubts but okay, I remove my downvote :) – Maxime Lorant Aug 30 '13 at 12:07
  • @MaximeLorant yes you are right. i doesn't mean that. i talked about `[(..,..)..` – suhailvs Aug 30 '13 at 12:08