Flask WTF validation False on first submit, True on second - validation

I am using the following code in a Flask-wtforms. I have tried with and without various validators in the SelectField but on the first time the user submits the form validation returns false.
I have also tried removing the extra validate method but still leaving a validator in the SelectField and again validation returns False on first submit.
Essentially I want to know if the SelectField is not set to a value of -1 (ie has been populated by the view method and presumably user is happy with the currently active item). I am not sure why the form if valid on second submit even though nothing else has been selected on the form
forms.py
something = SelectField(label = 'Something', choices = [('-1','Select Something')], default=('-1','Select Something'), id='select_something', validators=[validators.DataRequired(message='Nothing selected')])
#NB the line below with no validator also prints "--validate() returned false"
#something = SelectField(label = 'Something', choices = [('-1','Select Something')], default=('-1','Select Something'), id='select_something')
submit = SubmitField('Submit')
def validate(self):
rv = Form.validate(self)
if not rv:
print("--validate() returned false")
return False
#the line below never fired, see fix in following line
#if self.something.data == -1:
if str(self.something.data) == '-1':
logger.debug("--validate() data==-1")
return False
return True
view.py
form = Something(request.form)
if request.method == 'GET':
#get tuple_list from database
...
form.something.choices = tuple_list
form.something.default = tuple_list[0]
if request.method == 'POST' and form.validate():
print('Something added.')

I was using string instead of integers for the first part of each choices tuple (ie should be (1, 'text')) and not setting default correctly (just set default = n where n = integer).
Note the form.process() call as found here: How do you set a default value for a WTForms SelectField?
Fixes below:
views.py
form = Something(request.form)
#get tuple_list from database
...
form.something.choices = tuple_list
form.something.default = tuple_list[0][0] #integer id value
form.process()
if request.method == 'POST' and form.validate():
return self.render_template('it_works.html')
return self.render_template('select_something.html')
forms.py
#no validator used here
something = SelectField(label = 'Something', choices = [], id='select_something')
submit = SubmitField('Submit')
def validate(self):
if len(self.something.choices) == 0:
return False
return True

Related

Django rest framework does model field validation before own validation

I don't understand django rest framework validation process.
The doc say that validation is performed entirely on the serializer class.
However,
In the case of a PositiveIntegerField in a Model, when ModelSerializer receive a negative value, the model validation is processed before the custom ModelSerializer validate_myfield method.
class Blop(models.Model):
quantity = models.PositiveSmallIntegerField()
class BlopSerializer(serializers.ModelSerializer):
def validate_quantity(self, value):
return max(15, value)
class Meta:
model = Blop
if quantity if lesser than zero, validate_quantity is never called...
I've also tried :
to use model "clean" method but rest framework doesn't use it.
to disable min value validator in model by setting min_value to None, but it make rest framework to crash
to force MinValueValidator in model field : validators=[MinValueValidator(-math.inf, 'Minimum value is -Infinity')], it work, but it's too ugly
How can I do to have a serializer ensure non negative value in this case ?
I think your problem is that you are invoking the serializer.is_valid() method with no kwargs or raise_exception=False.
With raise_exception=True the serializer raises ValidationError, if you pass invalid data.
For better understanding, lets take a look at the Serializer.to_internal_value method.
def to_internal_value(self, data):
"""
Dict of native values <- Dict of primitive datatypes.
"""
if not isinstance(data, dict):
message = self.error_messages['invalid'].format(
datatype=type(data).__name__
)
raise ValidationError({
api_settings.NON_FIELD_ERRORS_KEY: [message]
})
ret = OrderedDict()
errors = OrderedDict()
fields = self._writable_fields
for field in fields:
validate_method = getattr(self, 'validate_' + field.field_name, None)
primitive_value = field.get_value(data)
try:
validated_value = field.run_validation(primitive_value)
if validate_method is not None:
validated_value = validate_method(validated_value)
except ValidationError as exc:
errors[field.field_name] = exc.detail
except DjangoValidationError as exc:
errors[field.field_name] = list(exc.messages)
except SkipField:
pass
else:
set_value(ret, field.source_attrs, validated_value)
if errors:
raise ValidationError(errors)
return ret
As you can see here, firstly, the serializer invokes the field.run_validation method, which uses django-fields validators and they raise DjangoValidationError exception and because of that, your validation method is never invoked.

trying to select a User in a TextInput in ModelForm in Django - and it won't work

I'm trying to create a webapp in django 1.9 for task tracking and ordering. The different tasks are divided into spaces (like different projects). Now, I want to be able to choose what the task is assigned to in the CreateView.
The problem is, that I have a large number of users in my system, so I do not want to show a dropdown. Instead, I want to use a TextInput widget, to have the form check for the available options (this way I can also use typeahead on the client side).
This is the best I could come up with for the TaskCreate view:
class TaskCreate(LoginRequiredMixin, CreateView):
"""
a view for creating new tasks
"""
model = Task
fields = ['space', 'name', 'description', 'assigned_to', 'due_date']
template_name = "task_tracker/task_form.html"
success_url = reverse_lazy('tracker:my_open_task_list')
def get_context_data(self, **kwargs):
context = super(TaskCreate, self).get_context_data(**kwargs)
context['header_caption'] = 'Create'
context['submit_caption'] = 'Create'
context['all_usernames'] = [x.username for x in User.objects.all()]
return context
def get_form(self, form_class=None):
form = super(TaskCreate, self).get_form(form_class)
form.fields['assigned_to'].choices = [(x.username, x.id) for x in User.objects.all()]
form.fields['assigned_to'].initial = self.request.user.username,
form.fields['assigned_to'].widget = widgets.TextInput()
try:
form.fields['space'].initial = Space.objects.get(name=self.request.GET['space'])
finally:
return form
def form_valid(self, form):
form.instance.created_by = self.request.user
form.instance.assigned_to = User.objects.get(username=form.cleaned_data['assigned_to'])
return super(TaskCreate, self).form_valid(form)
But the thing is that this is not working - the form still considers my choice to be illegal, even when I type in a valid username.
I tried to switch places the x.username and x.id in the choice field but it didn't help me.
I'm stuck on this for a week now. Can anybody help me please?

How to save a set of things over and over (autosave)

I am creating a set of things (each thing has FK to the set) directly with forms. The problem I am having is with the view(s).
I want to create the set for the things and then update all the things over and over using AJAX (Kind of like autosave). In my case the set is a SurveySet and the thing is a Survey.
def screen_many(request):
if not request.is_ajax():
# get an ordered QuerySet of students
students = ids_to_students(request.GET.items())
e_students = ids_to_students(request.GET.items(), 'e')
i_students = ids_to_students(request.GET.items(), 'i')
survey_count = len(students)
# Build a dataset of students with there associated behavior types.
data = [{'student':s.pk, 'behavior_type': 'E'} for s in e_students]
data += [{'student':s.pk, 'behavior_type': 'I'} for s in i_students]
# Use that dataset as initial data for a formset
SurveyFormset = formset_factory(SurveyForm, extra=0)
survey_formset = SurveyFormset(initial=data)
# ... not shown: customizing the crispy form helper
# Make a new survey set...
ss = SurveySet()
ss.user=request.user
ss.save()
if request.is_ajax():
surveyset = get_object_or_404(SurveySet, pk=ss.pk)
surveys = surveyset.survey_set.all()
survey_formset = SurveyFormset(request.POST, instance=surveyset)
if survey_formset.is_valid():
# Create surveys for this surveyset
for form in survey_formset.forms:
saved = form.save(commit=False)
saved.surveyset = ss
saved.save()
HttpResponse('saved.')
formsetWithStudents = zip(survey_formset.forms, students)
c = {
'formsetWithStudents' : formsetWithStudents,
'students' : students,
'survey_count' : survey_count,
'e_students': e_students,
'i_students': i_students,
'survey_formset': survey_formset,
}
c.update(csrf(request))
return render_to_response("reports/screen_many.html", c)
If my URL looks like this: http://127.0.0.1:8000/screen_many/?e_1=13&e_2=12&i_1=14 The view makes 3 survey sets all the while complaining that there is an
UnboundLocalError at /screen_many/
local variable 'ss' referenced before assignment
I feel like I need to make a separate view just for the ajax and I want the SurveySet object to only be created once.
So, in other words. I am filling in forms of a formset which update after clicking "view next form" This is in my template.
$('.next').click(function(){
$(this).parent().hide()
$(this).parent().next().show()
var posting = $.post('/screen_many/', $('form').serializeArray() );
posting.done(function(response){
console.log(response)
});
Or I could send the POST data here:
def save_as_you_go(request):
if request.is_ajax():
# Get the surveyset from POST
ss = request.POST['form-0-surveyset']
surveyset = get_object_or_404(SurveySet, pk=ss)
surveys = surveyset.survey_set.all()
SurveyFormSet = inlineformset_factory(SurveySet, Survey, form=SurveyForm, can_delete=False, extra=0)
survey_formset = SurveyFormSet(request.POST, instance=surveyset)
if survey_formset.is_valid():
for form in survey_formset.forms:
student = form.save(commit=False)
student.surveyset = surveyset
student.save()
return HttpResponse('saved.')
else:
return HttpResponseRedirect('/')
But then I get
[u'ManagementForm data is missing or has been tampered with']
Forgive me if my answer seems naive--I am new to Python and Django, but it looks like you are setting the ss variable in the non-ajax request and then referencing it in the ajax request. Perhaps you can set ss prior to the if statements?
#set ss variable before if statements
ss = SurveySet()
ss.user=request.user
ss.save()
if not request.is_ajax():
###do your non-ajax request stuff
if request.is_ajax():
###do your ajax request stuff

Django endless pagination with a request.method == 'POST'?

I'm trying to get Django Endless pagination to work on a search form.
All the examples and tutorials I've seen online show how to do it with a simple .all() queryset, but I need to filter out by the search results that I've had in the POST.
Here's how my view looks like for now:
#page_template("core/search_box.html")
def search(request,template = "core/search.html",page_template = "core/search_box.html",extra_context = None):
if request.is_ajax():
#template=page_template()
#users = Skill_User.objects.filter(skill__name__icontains=content).order_by('-level')
#return render_to_response( template , {'page_template': page_template,'menu_home_active':True, 'form':search_form, 'result':users} , context_instance )
return HttpResponse("AJAX")
elif request.method == 'POST':
search_form = SearchForm( request.POST )
# If Form is Valid
if search_form.is_valid():
type = search_form.cleaned_data['type']
content = search_form.cleaned_data['content']
print 'CONTENT ' + str(content)
if (type == 'O'):
users = Skill_User.objects.filter(skill__name__icontains=content).order_by('-level')
elif (type == 'G'):
users= {}
return render_to_response( template , {'page_template': page_template,'menu_home_active':True, 'form':search_form, 'result':users} , context_instance=RequestContext(request) )
else:
return HttpResponse("NOT OK")
So the search is done on POST. But how can I pass this search POST thingy to the ajax query that's done by the endless-pagination plug-in ? I commented out everything in the request.ajax() part, I just need "users" to be filled by the same data as after the POST request. Basically, for now when I scroll down I have "AJAX" showing up, and I'd like the rest of my query as well.
Any ideas? I haven't found anything obvious online about that. I come from a PHP background, and I would think about $_SESSION[] variables. Is there something similar in Django?
Ok, it's doable with a simple request.session[""] object.

Django form query

I saw this piece of code and have few questions..If anyone could explain that would be really helpful.
views.py
def search_page(request):
pdb.set_trace()
form = SearchForm()
bookmarks = []
show_results = False
if 'query' in request.GET:
show_results = True
query = request.GET['query'].strip()
if query:
form = SearchForm({'query': query})
bookmarks = Bookmark.objects.filter(title__icontains=query)
variables = RequestContext(request,{'form': form,
'bookmarks': bookmarks,
'show_results': show_results,
'show_tags': True,
'show_user': True})
return render_t7tr o_response('search.html', variables)
form.py
class SearchForm(forms.Form):
query = forms.CharField(label=u'Enter a keyword to search for', widget=forms.TextInput(attrs={'size':32}))
How do the below line of code work?
If 'query' in request.GET
how the 'query' string is in the request.Get?..When I debugged the dictionary contains the value contains the search value I have given.
THe code works fine but I want to understand.
Containment testing on mappings checks the keys.
key in d
Return True if d has a key key, else False.
EDIT:
Django parses the query string and populates request.GET from it.

Resources