How to make required field error message don't show up for first time in Django - django

First time I render a form I don't want the required error message to show up. Although if the field is left empty, it should prompt when submitting.
I know I can set an specific message and set it empty. But this way it never shows up:
error_messages = {'required':''}
I'm using a decorator to change label_tag behavior in BoundField, that makes an "*" show up next to the field label. But I need the error message to show up, only if field is empty.
I know I can check if field is required using:
{% if field.field.required %}
But I would need a way to know if the site is being rendered for the first time. For this I would like not to use an extra variable passed from the view or javascript. I have noticed that formsets actually work this way, but I don't want to put the form in a formset of one form

Error messages don't show up the first time anyway, if you're following the correct pattern in your view.
I suspect the error is showing because you're instantiating the form with a data parameter. You shouldn't do this when you're displaying it on the first GET. The proper way to do it is shown in the documentation.

Related

Add an additional form to a formset using POST without Javascript and validation

I want to use a Django formset, but with pure server rendering and without the need of Javascript to add additional forms to it. The user should just click a button on the page and the page should reload with an additional form in the formset. All user input should be preserved! The relevant part in the view is:
if request.POST.get('add_form') == "true":
cp = request.POST.copy()
cp['form-TOTAL_FORMS'] = int(cp['form-TOTAL_FORMS']) + 1
fs = MyFormSet(cp)
The problem is that when MyFormSet(cp) renders a form representation it adds validation errors to it (like "This field is required"). This is ugly and not acceptable. How can I render it without the errors (they should only be present when the whole form was submitted)?
MyFormSet(initial=...) seems not to be an option as it must also work in a UpdateView (the docs are pretty clear that initial is only for extra forms) and also the POST data can't be directly used as initial values.
I am super thankful for any hint as it took me several hours without getting anywhere (and it seems to be such a common feature as the rest of Django is so Javascript agnostic).
It feels like a hack, but this works (after the formset was initialized):
fs._errors = {}
for form in fs:
form._errors = {}
This removes all errors from the fields when it is rendered to HTML. The background is that when _errors is set to None (the default) the form validates itself when it is rendered. When it is set to an empty dict the form will not validate itself anymore and just "thinks" that there are no errors in it. So no error messages are rendered.

Delete rows set to be deleted in a formset and duplicates issues

I know this question has been asked before (e.g. Django modelformset_factory delete modelforms marked for deletion) but I've tried all the possible solutions, including doing what the official documentation says (https://docs.djangoproject.com/en/3.0/topics/forms/formsets/), and I still cannot delete the forms from my formset.
I have a form which correctly sends POST data with everything I need (including the DELETE instruction).
[print(form_links_event.cleaned_data) for form in form_links_event.deleted_forms]
[{'description': 'asdasd', 'link': 'http://www.test.com', 'id': <linksEvent: linksEvent object (25)>, 'DELETE': True}
Nevertheless, I need to process the formset before saving all the instances (I need to attach the id of a related model), so I need to call save(commit=False):
instances_links_event = form_links_event.save(commit=False)
for link in instances_links_event:
link.event = instance_event
link.save()
form_event.save()
form_links_event.save()
Doing so, though, strips the .deleted_forms list. In fact:
[print(instances_links_event.cleaned_data) for form in instances_links_event.deleted_forms]
AttributeError: 'list' object has no attribute 'deleted_forms'
Therefore I'm stuck in a loop: I cannot save my form directly because I need to attach more data to it first, and in its raw state it has the 'deleted_forms' list. Once I save it with commit=False and process with the processing, though, the 'deleted_forms' is not there any more so that I cannot delete those rows set for deletion. Ideally, I'd like to do this:
instances_links_event = form_links_event.save(commit=False)
for link in instances_links_event:
if (link.delete = True):
link.delete()
link.event = instance_event
link.save()
form_links_event.save()
I'm using Django 3.0.6 with Python 3.7.
Update
Even without the commit=False, saving the form with form_links_event.save(), I keep having issues: when I save the form in my 'edit page' (i.e. bound form) save() saves the existing records again, even if I didn't edit anything, which means I end up with a lot of duplicates. Is something wrong with Django formset or is it just me?
My form:
<tbody id='linksEvent_body'>
{% for formLink in form_links_event.forms %}
{{formLink.non_field_errors}}
{{formLink.errors}}
<trclass="formLink">
{{ formLink.id }}
<td>{{formLink.link}}</td>
<td>{{formLink.description}}{{formLink.DELETE}}</td>
</tr>
{% endfor %}
</tbody>
I've been battling with this and working for days on end, trying all sorts of possible parameters and code. I found out that the first time the form is loaded/bound after having saved it, it is not loading the form_management data correctly (i.e. the -INITIAL value is not correct and the extra blank form is not there) and also the bound data is not always updated. If I try to fix it via JavaScript, for example counting the number of forms displayed and put that number in the -CURRENT parameter and that number -1 in the -INITIAL or things like that, Django wouldn't like it (the documentation itself says that it is discouraged to tamper with the form management data anyway, which I understand). Once I'd manually refresh the page (by placing the cursor on the address bar and hit enter, not by hitting ctrl/cmd + R, which would ask me to send the POST data again), then the form loads correctly and any further edit would nicely save correctly. So, the only way I could find to solve this issue, which looks and sound like a Django inline forms bug to me, is by placing this code in the page containing the form:
<script type='text/javascript'>
(function()
{
if( window.localStorage )
{
if( !localStorage.getItem('firstLoad') )
{
localStorage['firstLoad'] = true;
window.location=window.location;;
}
else
localStorage.removeItem('firstLoad');
}
})();
</script>
With this code in place everything works like a charm. I hope this will help others who'll face this frustrating issue like myself.
PS: the issue is independent from the commit=False instruction. And yes, I declared #never_cache in my view.

ColdFusion 10 getting element is undefined in form error

I've got a ColdFusion 10 form, to which I'm adding a new multiple select list
<SELECT multiple="multiple" id="icd9list"
name="icd9list"
size="2"
class="pageText2"
style="width:400px;">
</SELECT>
It shows up on the form correctly and I can get items added to it with JavaScript, but when I go to process the form, the form action page is giving the following error:
Element ICD9LIST is undefined in FORM.
I've added cfparam tags on the initial form page, as well as the form action page where I'm getting the error.
Can't for the life of me figure out why it's not passing the form element to the action page. The method is post, so it should be picking it up. All of the other form elements on the page are picked up fine.
<cfform action="updform.cfm" name="custform" method="POST">
I also tried to use custform.icd9list and got the same issue:
Element ICD9LIST is undefined in CUSTFORM.
This page is some VERY legacy code that we can't really go back and refactor too much. The only thing I'm thinking is that when the page first loads, the multiple select option is blank and then we are adding options to it via javascript from a pop-out window after the fact. When I inspect the element in Chrome, after I've added options to it, they are there, but I'm wondering if it's still being treated as a blank multiple select list. I do know that if I remove the multiple attribute and treat the form element as a single drop down list that it shows up in the form dump with a value, but only the first (or whatever selected) value.
Beginning to think the answer may be to have a hidden field with the option values populated and let the form read that.
Adding options to a multiple select list isn't enough. They have to be selected as well. Otherwise, the list isn't considered a successful control and the field won't be passed to the action page (emphasis mine)
A successful control is "valid" for submission. Every successful
control has its control name paired with its current value as part of
the submitted form data set. A successful control must be defined
within a FORM element and must have a control name.
However:
Controls that are disabled cannot be successful.
If a form contains more than one submit button, only the activated submit button is successful.
All "on" checkboxes may be successful.
For radio buttons that share the same value of the name attribute, only the "on" radio button may be successful.
For menus, the control name is provided by a SELECT element and values are provided by OPTION elements. Only selected options may be
successful. When no options are selected, the control is not
successful and neither the name nor any values are submitted to the
server when the form is submitted.
The current value of a file select is a list of one or more file names. Upon submission of the form, the contents of each file are
submitted with the rest of the form data. The file contents are
packaged according to the form's content type.
The current value of an object control is determined by the object's implementation.
I suspect the form isn't forcing the items to be "selected" before the form is submitted and that's why nothing shows up on the action page.
It sounds like you need one of two things
Either required it on the client side
<SELECT multiple="multiple" required id="icd9list" name="icd9list" size="2" class="pageText2" style="width:400px;">
...
</SELECT>
Or make sure ColdFusion can handle it not existing on the server side.
<cfparam name="icd9list" default="">
As pointed out,
<cfparam name="form.icd9list" default="">
will get an even tighter scope
How are you calling on the form? Are you using form.variable or icd9list.variable? You shouldn't have to give it an ID or name unless there are multiple forms on the page. You can just name and ID your element and reference it like this: form.name

Django custom form field layout without manually redoing the whole form

I like Django form render(‘form’: form) and template {{ form }}, but it doesn’t allow you to arrange fields. I was just wondering if there was an easier way to do it, without rendering the whole form manually.
What Django does:
First Name
Middle Name
Last Name
What I want to do:
First Name Middle Name
Last Name
What makes Django {{ form }} so great is that it puts field labels and help text and all that jazz in the right places. Where as manually, you have to put all that in. If there isn’t an easier way, I’m ok with that, but I thought I would at least ask.
You could try to render individual fields of the form, like this part of the docs shows.

CSRF verification failed when input value changed by javascript

In my Django template I have a POST form. It includes the tag {% csrf_token %}. If I submit this form normally then everything works fine. However, I need to change a form input value via Javascript/Jquery:
$('input').val(id);
If I do this then I get the "CSRF verification failed" error message when I submit the form. I do submit the form via Javascript, but this has not been a problem so long as I don't dynamically change any values.
I have been "solving" this by writing #csrf_exempt above my Python functions. However, this skips the security. Is there another way?
Command $('input').val(id); overwrites all input entries, which can be found, so hidden csrf_token input loses its value. Try to make more specific selector. For example:
$('input[name=change-me]').val(id);