django custom form is invalid due to form init - django

I have the following form:
class PlayerAchievementForm(forms.ModelForm):
class Meta:
model = PlayerAchievement
fields = ('achievement',)
def __init__(self, *args, **kwargs):
super(PlayerAchievementForm, self).__init__(**kwargs)
self.fields['achievement'].queryset = Achievement.objects.filter(input_type=0)
I have the following implementation in a view:
def points(request, action, target=None):
if request.method == 'POST':
if target == 'player':
form = PlayerAchievementForm(request.POST)
print form.errors
if form.is_valid():
print 'valid'
elif:
print 'invalid'
On submit, this prints invalid.
If I take out this line in the form:
def __init__(self, *args, **kwargs):
super(PlayerAchievementForm, self).__init__(**kwargs)
self.fields['achievement'].queryset = Achievement.objects.filter(input_type=0)
Then it saves without issue. What is wrong with the init?

I found the answer here: DJango form with custom __init__ not validating
I was missing:
super(PlayerAchievementForm, self).__init__(*args, **kwargs)

I had the same problem and after a lot of tries, the code that work for me is something like that:
(Notice the first instantiation of the the field at class level.)
class PlayerAchievementForm(forms.ModelForm):
achievement = Achievement.objects.filter(input_type=0)
class Meta:
model = PlayerAchievement
fields = ('achievement',)
def __init__(self, *args, **kwargs):
super(PlayerAchievementForm, self).__init__(**kwargs)
self.fields['achievement'].queryset = Achievement.objects.filter(input_type=0)

Related

How to load a form with options from a queryset in Django

I am trying to load a form with user payment options, so this is needing a query set from the users profile.
I have tried initializing the form (below code) with user being required. The issue is if I make self.options when I am initializing. I have also tried creating the choice_field
class ListPaymentOptionsForm(forms.Form):
choice_field = forms.ChoiceField(widget=forms.RadioSelect, choices=options)
def __init__(self, user, *args, **kwargs):
self.options = list(UserPaymentOption.objects
.values_list('last_four', 'last_four')
.filter(user=user, active=True))
super(ListPaymentOptionsForm, self).__init__(self, *args, **kwargs)
The above code gives this error:
choice_field = forms.ChoiceField(widget=forms.RadioSelect, choices=options)
NameError: name 'options' is not defined
Then I have tried adding the options on the view instead like this
form = ListPaymentOptionsForm(user=request.user)
form.fields['choice_field'].choices = list(UserPaymentOption.objects
.values_list('id', 'last_four')
.filter(user=request.user, active=True))
This causes an error with the form being used on post, it seems like because it is trying to validate the value provided is a choice but in the actual form the choice is not set. The reason I believe this is the problem is this is what the form returns as
form=ListPaymentOptionsForm(request.POST)
print(form)
This returns: Choice field:Select a valid choice. 54 is not one of the available choices.
Any input on this would be very appreciated. Thanks.
Nearly there!
Try doing the fields['choice_field'].choices in the constructor.
class ListPaymentOptionsForm(forms.Form):
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs) # assuming python 3 constructor
self.options = list(UserPaymentOption.objects.values_list('last_four', 'last_four').filter(user=user, active=True))
self.fields['choice_field'] = forms.ChoiceField(widget=forms.RadioSelect, choices=self.options)
Maybe consider having a look at ModelChoiceField instead however, that way you can specify a queryset instead of having to worry about creating a list:
class ListPaymentOptionsForm(forms.Form):
choice_field = forms.ModelChoiceField(widget=forms.RadioSelect, queryset=UserPaymentOption.objects.none())
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['choice_field'].queryset = UserPaymentOption.objects.filter(user=user, active=True)
EDIT based on comments we can use the kwargs to pass the user which may be better:
class ListPaymentOptionsForm(forms.Form):
choice_field = forms.ModelChoiceField(widget=forms.RadioSelect, queryset=UserPaymentOption.objects.none())
def __init__(self, *args, **kwargs):
user = kwargs.pop('user') # this must be done before super()
super().__init__(*args, **kwargs)
self.fields['choice_field'].queryset = UserPaymentOption.objects.filter(user=user, active=True)
Then instantiate the form to handle POST data:
form = ListPaymentOptionsForm(request.POST, user=user)

Add a field value outside form in Django

Whenever I have to add a value to the instance of a form obtained from the context or from the URL I do it in the following way, using form.instance.
class PreguntaForm(forms.ModelForm):
class Meta:
model = Pregunta
fields = ('etiqueta', 'grupo', 'tipo_pregunta', 'opciones', 'mostrar_tabla', 'activo')
def __init__(self, *args, **kwargs):
cuestionario = kwargs.pop('cuestionario', False)
super(PreguntaForm, self).__init__(*args, **kwargs)
self.fields['grupo'].queryset = Grupo.objects.filter(cuestionario=cuestionario)
class PreguntaNueva(InfoPregunta, CreateView):
form_class = PreguntaForm
encabezado = 'Nueva Pregunta'
model = Pregunta
def get_form_kwargs(self):
kwargs = super(PreguntaNueva, self).get_form_kwargs()
kwargs['cuestionario'] = self.dame_cuestionario()
return kwargs
def form_valid(self, form):
form.instance.cuestionario = self.dame_cuestionario()
return super(PreguntaNueva, self).form_valid(form)
The problem that arises now is that I want to perform a check CreateView and EditView. To DRY, I want to do it in the clean method of the model, but the value that I assign to form.instance.cuestionario, is not available within the clean method. How could I do it? This value must not be edited by the user in any case.
Yes it is, you pass it in via get_form_kwargs; you just need to assign it to an instance variable in the form's __init__.
def __init__(self, *args, **kwargs):
self.cuestionario = kwargs.pop('cuestionario', False)
super(PreguntaForm, self).__init__(*args, **kwargs)
self.fields['grupo'].queryset = Grupo.objects.filter(cuestionario=self.cuestionario)
def clean(self):
# do something with self.cuestionario

Django 1.11 pass kwarg to modelform field

I would like to pass a kwarg to set a modelform field but im struggling to figure out how to do it.
My URL is as follows:
url(r'^tent/create/(?P<munc>\d+)',views.TentCreate.as_view(),name='tent_create'),
My view is simply:
class TentCreate(CreateView):
model = Tent
form_class = TentForm
And my form:
class TentForm(ModelForm):
class Meta:
model = Tent
exclude =('asfo','niho')
def __init__(self, *args, **kwargs):
super(TentForm, self).__init__(*args, **kwargs)
self.fields['primary'].queryset = Mark.objects.filter(munc=self.kwargs['munc'])
from the model:
class Tent(models.Model):
primary = models.ForeignKey(Mark,on_delete=models.CASCADE)
I can render the form fine without overriding def __init, with no filtering applied to the 'primary' field.
However attempting to use the def __init code I've described above to pass the munc kwarg to the form field is resulting in the following error:
"'TentForm' object has no attribute 'kwargs'"
I've been going around in circles trying to work through this so I would be really appreciative if anyone is able to provide me some guidance to solve this. This is my first Django project so I'm learning how I go so I assume I have made some fundamental error somewhere here!
Try overriding get_form_kwargs method:
views.py
class TentCreate(CreateView):
model = Tent
form_class = TentForm
def get_form_kwargs(self):
kwargs = super(TentCreate, self).get_form_kwargs()
kwargs.update({'munc': self.kwargs['munc']})
return kwargs
forms.py
class TentForm(ModelForm):
class Meta:
model = Tent
exclude =('asfo','niho')
def __init__(self, *args, **kwargs):
munc = kwargs.pop('munc')
super(TentForm, self).__init__(*args, **kwargs)
self.fields['primary'].queryset = Mark.objects.filter(munc=munc)
class TentCreate(CreateView):
form_class = TentForm
def get_form(self, form_class=None):
if form_class is None:
form_class = self.get_form_class()
kwargs = self.get_form_kwargs()
print(kwargs, self.kwargs)
kwargs.update(self.kwargs)
return form_class(**kwargs)
forms.py
class TentForm(ModelForm):
class Meta:
model = Tent
exclude =('asfo','niho')
def __init__(self, *args, **kwargs):
munc=self.kwargs['munc']
super(TentForm, self).__init__(*args, **kwargs)
self.fields['primary'].queryset = Mark.objects.filter(munc=munc)
you must pop munc before call super(TentForm, self).__init__(*args, **kwargs)

DJango form with custom __init__ not validating

When I use this code to customize my form's widget, it will not validate. If I comment out def __init__(..) it works fine.
class CommentForm(forms.Form):
def __init__(self, *args, **kwargs):
self.wysiwyg = kwargs.pop('wysiwyg', False)
super(CommentForm, self).__init__()
if self.wysiwyg:
self.fields['comment_text'].widget = SummernoteWidget()
else:
self.fields['comment_text'].widget = forms.Textarea(attrs={'rows':2})
comment_text = forms.CharField()
I've been able to troubleshoot this far, and the difference between the working form (no init) and the invalid form, is this:
not valid form with init:
CommentForm bound=False, valid=Unknown, fields=(comment_text)
valid form:
CommentForm bound=True, valid=Unknown, fields=(comment_text)
Is bound the problem and how do I fix it?
Thanks!
Try this .. might work
class CommentForm(forms.Form):
def __init__(self, *args, **kwargs):
try:
self.wysiwyg = kwargs['wysiwyg']
except KeyError:
self.wysiwyg = None
super(CommentForm, self).__init__(*args, **kwargs)
if self.wysiwyg:
self.fields['comment_text'].widget = SummernoteWidget()
else:
self.fields['comment_text'].widget = forms.Textarea(attrs={'rows':2})
comment_text = forms.CharField()

Django ModelForm with field overwrite does not post data

So, I have the following form:
class DesignItemForm (forms.ModelForm):
def __init__(self, *args, **kwargs):
super(DesignItemForm, self).__init__(*args, **kwargs)
CHOICES=[(i,i) for i in range(MAX_DESIGN_ITEM_QUANTITY)]
self.fields['quantity'] = forms.ChoiceField(choices=CHOICES)
class Meta:
model = DesignItem
fields = ('quantity','trackable',)
My view:
d = Design.object.get(slug=fromInput)
....
DesignItemInlineFormSet = inlineformset_factory(Design, DesignItem, fk_name="design", form=DesignItemForm,)
if request.method == "POST":
formset = DesignItemInlineFormSet(request.POST, request.FILES, instance=d)
if formset.is_valid():
formset.save()
DesignItemInlineFormSet(instance=d)
As you can tell, in my form, I overwrote the quantity field to be a drop down instead of an integer field.
For some reason, when I submit the form, the data is not updated in the database. However, if I change the form to the following, it works (of course, it doesn't have the dropdowns I want, but it posts to the db). Why is this, and how do I fix it?
class DesignItemForm (forms.ModelForm):
def __init__(self, *args, **kwargs):
super(DesignItemForm, self).__init__(*args, **kwargs)
# CHOICES=[(i,i) for i in range(MAX_DESIGN_ITEM_QUANTITY)]
# self.fields['quantity'] = forms.ChoiceField(choices=CHOICES)
class Meta:
model = DesignItem
fields = ('quantity','trackable',)
EDIT: Here is the DesignItem model:
class DesignItem(models.Model):
"""Specifies how many of an item are in a design."""
design = models.ForeignKey(Design, related_name="items")
quantity = models.PositiveIntegerField(default=1)
trackable = models.ForeignKey(Trackable, related_name="used")
have you tried just overriding the widget instead of the whole field?
i guess you want a select widget
def __init__(self, *args, **kwargs):
super(DesignItemForm, self).__init__(*args, **kwargs)
CHOICES=[(i,i) for i in range(MAX_DESIGN_ITEM_QUANTITY)]
self.fields['quantity'].widget = forms.Select(choices=CHOICES)