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

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.

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.

CFWheels: Display form errors on redirectto instead of renderpage

I have a form which I am validating using CFWheels model validation and form helpers.
My code for index() Action/View in controller:
public function index()
{
title = "Home";
forms = model("forms");
allforms = model("forms").findAll(order="id ASC");
}
#startFormTag(controller="form", action="init_form")#
<select class="form-control">
<option value="">Please select Form</option>
<cfloop query="allforms">
<option value="#allforms.id#">#allforms.name#</option>
</cfloop>
</select>
<input type="text" name="forms[name]" value="#forms.name#">
#errorMessageOn(objectName="forms", property="name")#
<button type="submit">Submit</button>
#endFormTag()#
This form is submitted to init_form() action and the code is :
public function init_form()
{
title = "Home";
forms = get_forms(params.forms);
if(isPost())
{
if(forms.hasErrors())
{
// don't want to retype allforms here ! but index page needs it
allforms = model(tables.forms).findAll(order="id ASC");
renderPage(action="index");
//redirectTo(action="index");
}
}
}
As you can see from the above code I am validating the value of form field and if any errors it is send to the original index page. My problem is that since I am rendering page, I also have to retype the other variables that page need such as "allforms" in this case for the drop down.
Is there a way not to type such variables? And if instead of renderPage() I use redirectTo(), then the errors don't show? Why is that?
Just to be clear, I want to send/redirect the page to original form and display error messages but I don't want to type other variables that are required to render that page? Is there are way.
Please let me know if you need more clarification.
This may seem a little off topic, but my guess is that this is an issue with the form being rendered using one controller (new) and processed using another (create) or in the case of updating, render using edit handle form using update.
I would argue, IMHO, etc... that the way that cfWheels routes are done leaves some room for improvement. You see in many of the various framework's routing components you can designate a different controller function for POST than your would use for GET. With cfWheels, all calls are handled based on the url, so a GET and a POST would be handled by the same controller if you use the same url (like when a form action is left blank).
This is the interaction as cfwheels does it:
While it is possible to change the way it does it, the documentation and tutorials you'll find seem to prefer this way of doing it.
TL; DR;
The workaround that is available, is to have the form be render (GET:new,edit) and processing (POST:create,update) handled by the same controller function (route). Within the function...
check if the user submitted using POST
if it is POST, run a private function (i.e. handle_create()) that handles the form
within the handle_create() function you can set up all your error checking and create the errors
if the function has no errors, create (or update) the model and optionally redirect to a success page
otherwise return an object/array of errors
make the result error object/array available to view
handle the form creation
In the view, if the errors are present, show them in the form or up top somewhere. Make sure that the form action either points to self or is empty. Giving the submit button a name and value can also help in determining whether a form was submitted.
This "pattern" works pretty well without sessions.
Otherwise you can use the Flash, as that is what it was created for, but you do need to have Sessions working. their use is described here: http://docs.cfwheels.org/docs/using-the-flash and here:http://docs.cfwheels.org/v1.4/docs/flashmessages
but it really is as easy as adding this to your controller
flashInsert(error="This is an error message.");
and this to your view
<cfif flashKeyExists("error")>
<p class="errorMessage">
#flash("error")#
</p>
</cfif>

Django/Haystack - best option to have a listview with search capability

I have an app with a Restaurant model. I'd like to understand what is the best way to put together a view that displays the list of restaurant objects, but also has a search form above that a user could enter parameters to filter the results displayed. If no parameters are entered, all the restaurants should be shown. I'm already using haystack and have a search form, but currently it is on a standalone search.html template. I also have an ListView on a separate template, and I guess I'm looking for an end result that combines these.
I did some reading on line and it's unclear what the best way to do it is:
using just listview from Django with some queryset filtering
combining haystack SearchView with django class based views?
this is my best bet so far - creating a customized version of SearchView from Haystack
ideally, ultimately the search capabilities would include things like allow autocompleting the user's inputs, and dynamically filter the results as the user types.
any thoughts on what the best way is to go about this and any examples out there?
There probably is out there some package that gives you everything automatically, displaying the queryset list and allowing simple adding of a search bar and so on (like the admin site). But I take it you are still a beginner to web development, so I'd strongly suggest you drop the listviews, drop haystack, drop everything, and do it all yourself.
See, they're not bad approaches or anything, but they're like shortcuts. And shortcuts are good only if they shorten the original way, which in your case isn't all that long.
To give you an example, here's a simple approach to displaying a list of items:
views.py
def restaraunt_list(request):
restaraunts = Restaraunt.objects.all().order_by('name')[:100] # Just for example purposes. You can order them how you'd like, and you probably want to add pagination instead of limiting the view number arbitrarily
context = {'restaraunts': restaraunts}
return render_to_response('index.html', context)
index.html:
<ul>
{% for rest in restaraunts %}
<li>{{ rest }}</li>
{% endfor %}
</ul>
That's it. Now it displays it in a list. Now to add a search filter all you need to do is this:
index.html (add this anywhere you want it)
<form>
<input type='text' name='q' id='q'></input>
<input type='submit' value='search!'></input>
</form>
When a user sends the data from 'q' it is sent through GET, and then on server side you add this:
def restaraunt_list(request):
restaraunts = Restaraunt.objects.all()
# filter results!
if request.method == 'GET':
q = request.GET['q']
if q != '':
restaraunts = restaraunts.filter(name__contains=q)
# ordering is the last thing you do
restaraunts = restaraunts.order_by('name')[:100]
context = {'restaraunts': restaraunts}
return render_to_response('index.html', context)
Now, this will work, but from what you wrote I understand you want the search to be live the moment a key is pressed. For that, you'd need to use AJAX (go learn how it works, I'm not gonna delve into it here). As for autocomplete, you should check out jQuery UI.
But, like I said, before jumping ahead and using all those stuff, I suggest you first learn the basics. Go through the django tutorial (if you haven't already), and use the amazingly detailed django-docs every step of the way. When some specific things won't work and you're stuck, come here to Stackoverflow and someone will surely help you. good luck!

Django: use POST or links, which is better practice?

A noobish question to be sure.
<a href="{% url 'stuff.views.SomeView' %}/somethingnew">
<button>See something new on this page</button>
</a>
<form action="" method="post">{% csrf_token %}
<button name="somethingnew" type="submit" value=True>See something new on this page</button>
</form>
With either choice, I update some boolean variable, perform the appropriate calculations, call the page view and render a page with something new on this page. Part of the reason I use either method is to save the state of a collection of boolean variables. What is the best way 1) change a boolean variable 2) save its state 3) perform the necessary updates when the button is clicked and finally 4) render page after the underlying data has been updated?
Right now, I am using forms rather than links so that I don't need to code a url for each boolean variable. Which method is better? Will one method improve the time it takes to reload the page (assuming many boolean variables)?
1) Following the REST mindset, a POST request is in order to transmit the user input, since you are altering database objects.
2) I'd save it in the Session object if the input is not needed forever (session duration). Otherwise in the database as you are doing now.
3/4) I'd gather all the necessary info in a form. When the user commits the form in a POST request, I'd compute the data and respond with the rendered page containing the computed result. If the input variables are gathered step by step with intermittent computation, I'd just update the input form accordingly (display different choices in a combo box or something like that). Of course the transmitting could be done in an AJAXy way, too.

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.