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

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.

Related

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.

Can I generate a form depending on the results of a view?

I need to create a form which depends on the results of a view. I can generate it within the view and/or create a view template in order to do this....But how do I process it?
How do I connect my form/template generated form to a form_submission function?
When this custom/dynamic form is submitted I need to call some drupal functions to create some content in the site.
I did not find any way to do it in a view, but what I did was:
- Create a form programatically, making only a hidden field for NIDS
- Place my view next to that form using AJAX
- When the view is updated I copy the results of the view to the NIDS field and submit the form (This reloads the page)
- Form submission has 2 behaviours, 1 to re-generate the form when provided with NIDs, and other one for the actual form created by the NIDS result of my view.
Just in case this helps someone

'Hiding' form query from URL (Django 1.3)

I have a form with 6-7 fields. After user input, my webapp searches for those fields in a database and displays the results.
Now the issue is, that the URL ends up having all the form field names and their values in it.
result/?name=lorem&class=arc&course=ipsum
Now with the form having 7-8 fields the url ends up looking ugly.
Is there a Django technique to 'hide' these from the URL? Quotes around hide because I'd be okay with a completely different way to pass the objects to my database from the form as well.
Use a POST request. Here's the django docs on forms and a specific example using POST>. HTML-wise, all you need to do is change the method on the form tag.
I do not recommend to use POST requests for search. If you'll use GET it will be easer for user, he can just bookmark a link and save search or share search results with friends.

How to make required field error message don't show up for first time in 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.

How to make fields readonly while updating

I have a form. Once the form is filled I don't want the user to change anything in the form.
But the user can see the values. Meaning all the fields are non editable. I can do this by using instance method but this does not help in foreignkey.
Depends on what you mean "once the form is filled".
If it's an html form post, just render a new html page with simple text and values of the submitted form.
If the post-back was an ajax call, you can change the CSS-styling of the elements for example and disable the submit button or erase the whole and substitute the values that you get back from ajax request.
There is no "editable=False" property on html input elements btw.
you can use readonlyAdmin