Dynamic ChoiceField unable to be validated in form - python

I have a form that's being given a dictionary of selection, it populates it correctly but on form submit it is not valid. When attempting to print errors, non_field_errors there are just blanks. When I am redirected to the form, now the choice field is populated by one choice and the csrf token from previous submit.
I've tried assigning choices in different ways such as self.fields['calendar'] = forms.ChoiceField(choices=choice_list) directly assign in a different way. self.fields['calendar'].choices = choice_list, a custom validator that ignores the validation, and inline debugging.
Form model:
class CalendarSelectionForm(forms.Form):
calendar = forms.ChoiceField(label="Calendar")
def __init__(self, calendars=None, *args, **kwargs):
super(CalendarSelectionForm, self).__init__(*args, **kwargs)
choice_list = [(calendar_id, calendar_name) for calendar_id, calendar_name in calendars.items()]
if calendars:
self.fields['calendar'].choices = choice_list
View:
if request.method == "POST":
print(request.POST)
cal_sync_form = CalendarSelectionForm(request.POST)
print("Non-field errors " + str(cal_sync_form.non_field_errors()))
print("Reg form errors " + str(cal_sync_form.errors))
# print("Field val " + str(cal_sync_form.calendar))
print("Field data " + str(cal_sync_form.data))
print("Field fields " + str(cal_sync_form.fields) + " Form is " + str(cal_sync_form.is_valid()))
if cal_sync_form.is_valid():
data = cal_sync_form.cleaned_data
print(data)
return render(request, 'management/gcal_sync_dashboard.html')
else:
return render(request, 'management/acct_select.html', {'form': cal_sync_form})
Form template:
<form class="form-import" action="/manage/gcal/sync/" method="post" id = "">
{% csrf_token %}
{{ form.calendar }}
{{ form.errors }}
{{ form.non_field_errors }}
<div class="push clearfix"></div>
<div class="col-sm-6 no-pad push"><input class="btn btn-brand btn-little button filson push-half" type="submit" value="Select email"><i class="fa fa-plus"></i></input>
</div>
</form>
The goal is to validate a posted form, the current print statements print out
<QueryDict: {'csrfmiddlewaretoken': ['sJHE8JJAzmeS0nRjaYZg5KdMlevJiInYY0G4YFJeITH1cVjciIdR1Dq1N28loUIL'], 'calendar': ['email#email.io']}>
Non-field errors
Reg form errors
Field data {}
Field fields OrderedDict([('calendar', <django.forms.fields.ChoiceField object at 0x117323080>)]) Form is False

In your view, you make a call to the CalendarSelectionForm constructor with request.POST as first positional argument. So that means that you call the __init__ function, and request.POST is passed as the calendars parameter.
You can fix this by constructing your form with named parameters. You furthermore will need to pass the same parameter to calendars as you did when you rendered the form with the GET request, since otherwise the choices do not per se match, and the user might have picked an option that is in that case not available during the POST request. Like:
if request.method == 'POST':
cal_sync_form = CalendarSelectionForm(calendars=my_calendars, data=request.POST)
# ...
with my_calendars the same value you pass when you constructed the form in the GET case.

Related

Dynamically populate a choice field and get the non-chosen fields in the post data

I have a form where I'm trying to compare two objects (Cards) that are randomly selected from the database. In my form, I tried to dynamically populate a choicefield with the two cards, and in the view I'm trying to create an object (CardComparison(winning_card,losing_card)) with the choice of the user.
I've overwritten the __init__ of the form to dynamically populate the card choices, and it's working fine. The problem is that when the user selects a card, it only passes the selected card, and I'm not able to figure out which card is NOT chosen in the view.
I'm new to Django, and I've realized the more I struggle with this, a dynamic choice field may not be what I actually want to use, so a suggestion for a better method would also be greatly appreciated.
forms.py:
def get_new_comparison():
left_card = #get a random card (code hidden for cleanliness)
right_card = #get a second random card
left_card_choice = (left_card, left_card.name)
right_card_choice = (right_card, right_card.name)
return [left_card_choice, right_card_choice]
class CompareCardsForm(forms.Form):
def __init__(self, *args, **kwargs):
post_flag = False
if kwargs.get('post_flag'):
post_flag = kwargs.pop('post_flag')
super(CompareCardsForm, self).__init__(*args, **kwargs)
if post_flag and len(args) > 0:
card_name = args[0].get('cards_to_compare')
card_obj = Card.objects.get(name=card_name)
card_choice = [(card_obj,card_name)]
self.fields['cards_to_compare'] =
forms.ChoiceField(choices=card_choice, widget=forms.RadioSelect())
#Because everytime the __init__ is called the choices are randomized, I need to set the choices to the POST data's choices otherwise the selected card may not be in the form when it's trying form.is_valid()
else:
self.fields['cards_to_compare'] = forms.ChoiceField(choices=get_new_comparison(), widget=forms.RadioSelect())
views.py:
def CompareCards(request):
if request.method == 'POST':
form = CompareCardsForm(request.POST, post_flag=True)
if form.is_valid():
print(form.cleaned_data['cards_to_compare'])
# Here is where I want to create a new comparison using the two cards
return HttpResponseRedirect(reverse('comparecards'))
else:
print(form.errors.as_text())
else:
form = CompareCardsForm()
return render(
request,
'mtg_compare/comparecard.html',
{'form': form}
)
The solution to this is actually by using the template file. By adding hidden fields in the template file and by setting the choices to an accessible variable in the form, you can pull the fields and add them to the POST message, so that I'm able to access them in the view.
comparecard.html:
{% extends "base_generic.html" %}
{% block content %}
<h1>Compare Cards</h1>
<h2>Choose a card to win the matchup</h2>
<p>
<p>
<form action="" method="post">
{% csrf_token %}
<table>
{{ form.cards_to_compare }}
</table>
<input type="hidden" value="{{ form.card_list }}" name="var_name_1" />
<input type="submit" value="Submit" />
</form>
{% endblock %}
forms.py:
class CompareCardsForm(forms.Form):
def get_fresh_comparison():
return get_new_comparison()
def __init__(self, *args, **kwargs):
post_flag = False
if kwargs.get('post_flag'):
post_flag = kwargs.pop('post_flag')
super(CompareCardsForm, self).__init__(*args, **kwargs)
if post_flag and len(args) > 0:
card_name = args[0].get('cards_to_compare')
card_obj = Card.objects.get(name=card_name)
card_choice = [(card_obj,card_name)]
self.card_list = card_choice
self.fields['cards_to_compare'] = forms.ChoiceField(choices=card_choice, widget=forms.RadioSelect())
else:
self.card_list = get_new_comparison()
self.fields['cards_to_compare'] = forms.ChoiceField(choices=self.card_list, widget=forms.RadioSelect())
views.py:
def CompareCards(request):
if request.method == 'POST':
form = CompareCardsForm(request.POST, post_flag=True)
if form.is_valid():
print(request.POST.get('var_name_1'))
return HttpResponseRedirect(reverse('comparecards'))
else:
print(form.errors.as_text())

invalid django form makes is_valid method always return false

My django form is invalid and so the .is_valid method never returns true. As a result, I am getting an "Expected HttpResponse but received None" type of error because my code never executes what is within the if-condition. I am wondering how to make my form valid. I am new to django so I am probably missing something obvious. Here is my code:
views.py
template_name1 = 'multiplication/detail.html'
template_name2 = 'multiplication/multiplied.html'
class myForm(forms.Form):
quantity1 = forms.IntegerField(required=False)
quantity2 = forms.IntegerField(required=False)
form = myForm()
def get(request):
return render(request,template_name1,{'form': form} )
def multiply_two_integers(x,y):
return x*y
def post(request):
if (form.is_valid()):
x = request.POST.get('quantity1')
y = request.POST.get('quantity2')
product = multiply_two_integers(x, y)
return render(request, template_name2, {'form': form, 'product':
product })
template_name1
<h1>Multiplication Function</h1>
<form action = "{% url 'multiplication:post' %}" method = "post">
{{ form.as_p }}
{% csrf_token %}
<input type = "submit" value ="Multiply">
<!--<button type="submit"> Multiply </button>-->
<h1>{{product}}</h1>
</form>
template_name2
<h1>{{product}}</h1>
urls/multiplication
from django.urls import path
from multiplication import views
app_name = 'multiplication'
urlpatterns = [
# /multiplication/
path('', views.get, name = 'get'),
path('multiplied', views.post, name='post')
]
This code is very strange. You seem to have a set of functional views, but are trying to randomly use some concepts from class-based views.
The reason why your form is not valid is because you never pass any data to it; an unbound form cannot be valid. You should not be instantiating the form outside of a view; you need to do it in the view, and when the request is a POST you should pass the POST data to it.
In function-based views you should not define separate functions for get and post. Combine them, as sown in the Django docs.
There is another point that you have missed about the error message; your reaction to it telling you that you have not returned a response if the form is invalid is to ask "why isn't it valid", but you should also do what it says and return a response in this case; the form will sometimes be actually invalid, and you should deal with this case.
Finally, to get the data from the form you should use form.cleaned_data, not request.POST.
def multiply_two_integers(x,y):
return x*y
def my_view(request):
if request.method == 'POST':
form = MyForm(request.POST)
if (form.is_valid()):
x = form.cleaned_data['quantity1']
y = form.cleaned_data['quantity2']
product = multiply_two_integers(x, y)
return render(request, template_name2, {'product': product })
else:
form = MyForm()
return render(request,template_name1,{'form': form} )

Django: edit the choices set of forms.Select widget with dB data linked to current user in templates

Dear Python community,
Could you please share some insights with a newbie like myself regarding the following topic:
I would like to dynamically modify the inputs into form field input, specifically
forms.ChoiceField(choices=((text, name), widget=forms.Select())
As I was not able to access requestfrom class in forms.py, I'd like to try editing the choices from Django template engine. Is it possible to edit the choices, taking the parameters from views.py method using jinja?
The question is conceptual, a few lines of code as an example would be enough, I'll pick it up.
The tricky part is - the data should be dependent on logged-in User's created model instances.
If there's no actual way to do it via python, but js only - please let me know so I don't dry to do the impossible.
Thank you!
Code sample for reference:
forms.py
class InformForm(forms.Form):
flight_number = forms.CharField(5, widget=forms.TextInput())
date = forms.DateField(widget=forms.DateInput(attrs={'class': 'datepicker'}))
template = forms.ChoiceField(choices=tuple([(template.text, template.name) for template in Template.objects.all()]),
widget=forms.Select(attrs={'id': 'select_box',
'onchange': 'javascript:changer();'}))
text = forms.CharField(widget=forms.Textarea(attrs={'id': 'txt_box', 'class': 'latin',
'maxlength': "160", 'onchange': 'javascript:validateTextArea();'}))
template
<form class="form-signin form-container" method="post" enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
<div class="form-element-wrapper">
<div class="error-form-element">
<span class="error-span">{{field.errors}}</span>
</div>
<div class="form-label">{{field.label_tag}}</div>
<div class="form-data">{{field}}</div>
</div>
{% endfor %}
<button id="cr_inf" type="submit" class="btn btn-lg btn-primary btn-block stl-color"><span id="loader" class=""></span>Create inform</button>
</form>
views.py
class InformFill(View):
form_class = InformForm
temlate_name = 'distrib_db/inform_fill.html'
def get(self, request):
if request.user.is_authenticated():
form = self.form_class(None)
return render(request, self.temlate_name, context={'form': form})
else:
return redirect('distrib_db:login')
def post(self, request):
if request.user.is_authenticated():
form = self.form_class(user=request.user, data=request.POST)
if form.is_valid():
inform = Inform(flt_numbr=form.cleaned_data['flight_number'], date=form.cleaned_data['date'],
template=form.cleaned_data['text'], request=request)
inform.save()
date = form.cleaned_data['date']
flt_numbr = form.cleaned_data['flight_number']
try:
emails, contacts = get_mail_cnt(date, flt_numbr)
# inform = get_object_or_404(Inform, pk=request['pk'])
paxdata = PaxData(inform=inform, emails=' '.join(emails), contacts=' '.join(contacts))
paxdata.save()
return redirect('/inform/{0}/'.format(inform.pk))
# 'distrib_db:detail', context={'pk': inform.id}
except Exception as e:
return render(request, 'distrib_db/sample.html',
context={'date': date, 'flight_number': flt_numbr, 'error': e})
# return render(request, 'distrib_db/sample.html', context={'date': date, 'flt_numbr': flt_numbr})
return render(request, self.temlate_name, context={'form': form})
else:
return redirect('distrib_db:login')
QuerySet issue:
>>> usr = User.objects.filter(username='aleks')
sample = tuple([(template.text, template.name) for template in usr.template_set.all()])
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'QuerySet' object has no attribute 'template_set'
In InformForm class override __init__
def __init__(self, user, *args, **kwargs):
super(InformForm, self).__init__(*args, **kwargs)
self.fields['template'] = forms.ChoiceField(choices="make choice based on user")

Django: How to pass variable on form submit

I have a django 1.6.11 form (views.py):
def posneg_nlp(request):
sys_project_name = request.GET.get('project', 'graph') # here oll is ok, it can get correct project value
success = False
monitoring_words = ''
pos_features = ''
neg_features = ''
date_saved = datetime(2015, 7, 29)
print('posneg_nlp form')
print("posneg_nlp request.GET.get('sys_project_name', 'graph')", request.GET.get('project', 'graph'))
if request.method == 'POST':
posnegnlp_form = PosnegnlpForm(request.POST)
if posnegnlp_form.is_valid():
print('posneg_nlp form is_valid')
success = True
sys_project_name = sys_project_name
# here it cannot get project value, it replaced with default:
print("posneg_nlp form is_valid request.GET.get('sys_project_name', 'graph')", request.GET.get('project', 'graph'))
print("sys_project_name ", sys_project_name)
monitoring_words = posnegnlp_form.cleaned_data['monitoring_words']
pos_features = posnegnlp_form.cleaned_data['pos_features']
neg_features = posnegnlp_form.cleaned_data['neg_features']
print('pos_features:', pos_features, 'neg_features:', neg_features)
posneg_nlp_filter(sys_project_name, pos_features, neg_features, db_collection=Vkwallpost)
#get_likes_wallposts_by_owner_id(typeobject='post', owner_id=None, item_id=None, filter_posts='likes')
else:
posnegnlp_form = PosnegnlpForm()
success = False
ctx = {'posnegnlp_form': posnegnlp_form, 'sys_project_name': sys_project_name, 'monitoring_words': monitoring_words,
'pos_features': pos_features, 'neg_features': neg_features, 'success': success}
return render_to_response('choose_nlp_filter.html', ctx, context_instance = RequestContext(request))
This is the second form among two. From first form i pass a variable sys_project_name to this form via template:
<div class="rowSubmit">
<a style="outline: medium none;" hidefocus="true" href="{{ DOMAIN_URL }}/post/choose_nlp_filter/?project={{ sys_project_name }}" class="btn btn-right"><span class="gradient">К шагу 2. Выбор фильтров </span></a>
</div>
When i print current value of sys_project_name in form function posneg_nlp(request) above it shows correct value request.GET.get('project', 'graph') equal to graph2 (happens on form render).
But after that after if posnegnlp_form.is_valid(): it stops to see it and request.GET.get('project', 'graph') shows value in case it not found, equal to "graph".
So, how to pass variable and dont allow to rewrite it?
In the first case, the view is responding to an http GET request, so request.GET contains your project parameter. When the form is submitted, now the view is responsing to an http POST request, and request.POST contains the form data. In the latter case, if you want request.GET to still contain the 'project' parameter, then you can pass it via the form action parameter in your form tag:
form action="/some/url/?project={{ sys_project_name }}"

New to DJango need to fill form with initial data in get_data

Here is my html:
{% block my_dashboard_main %}
<form action="status/" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
{% endblock %}
My urls.py:
urlpatterns = patterns('',
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'status/$', views.status),
url(r'thanks/$', views.thanks),
)
Here is my views.py:
STATUS_CHOICES = (
("GOOD", "Good"),
("BAD", "Bad"),
("COMPROMISED", "Compromised")
)
def thanks(request):
return render(request, "my_dashboard/ssa_panel/sent.html')
class SsaForm(forms.Form):
status = forms.ChoiceField(choices = STATUS_CHOICES, label="Status:")
def status(request):
print("STATUS CALLED method=",request.method)
if request.method == 'POST': # If the form has been submitted...
form = SsaForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
print("redirect to THANKS!")
return HttpResponseRedirect('thanks/') # Redirect after POST
else:
print("Requesting form\n")
form = SsaForm(initial = {"status", "Good"}) # An unbound form
return render(request, 'my_dashboard/ssa_panel/index.html', {
'form': form,
})
class IndexView(views.APIView):
# A very simple class-based view...
template_name = 'my_dashboard/ssa_panel/index.html'
def get_data(self, request, context, *args, **kwargs):
print("GET_DATA Called", context)
# Add data to the context here...
return context
The first time my page renders the I want the status to show up. It doesn't. Just the Submit button. After I submit once the "Status: [Good] <- combo box" is there. I want to go get the data for the for status in get_data and set it but I don't know how. do I set context['status']="Good" or something like that?
I'm obviously new to DJango and REST stuff.
You are trying to construct your initial value dictionary incorrectly using a comma (,) instead of a colon and also using the wrong choice key. Instead of
form = SsaForm(initial = {"status", "Good"})
try
form = SsaForm(initial = {"status": "GOOD"})

Categories

Resources