How to dynamically delete object using django formset - python

Django says, i should render inline formset this way:
{{ formset.management_form }}
{% for form in formset %}
{{ form.id }}
{{ form.field_1 }}
{{ form.field_2 }}
<button type="button"> delete </button>
{% endfor %}
<button type="submit"> submit </button>
Ok. But what if i want to delete some formset objects (form) dynamically? User press delete button - i remove form from the DOM, i use ajax to remove object, related to the form from the DATABASE. It works ok till this point. But when user clicks submit - my views.py trying to validate formset:
filled_formset = OrderItemFormSet(request.POST, instance=order)
if filled_formset.is_valid():
and raises error:
MultiValueDictKeyError at /order/cart/
"'orderitem_set-0-id'"
...\market\ordersys\views.py in show_cart
59. if filled_formset.is_valid():
I think it happend because form objects were displayed by django with some regularity (first form got id = orderitem_set-0-id, second = orderitem_set-1-id etc.) And when i deleted first form from the DOM, the regularity was broken - there is no more form with orderitem_set-0-id. But is_valid() still trying to get it dict["orderitem_set-0-id"].
I could use some black magic, substituting django's technical information, displayed in the DOM, restoring disrupted regularity, but is there a better way?
Could you tell me how to properly dynamically delete formset items, please ?

I got no answer for some time, so i did not find any better solution than below. Maybe someome will find it useful.
Ok, the trick is - to have {{form.DELETE}} for any form in your formset in template. It renders as a checkbox (i made it invisible) and i made JS to make it "checked" whenever user press "delete" button. After user press "submit" button, every form, marked for deletion, will NOT be validated by the view during filled_formset.is_valid(). This lets you delete object from database with ajax behind the scene.
The problem was that an ERROR was raised during formset validation. Caused by the form of an object, which was already deleted from database with ajax.
So there are all components:
views.py
def show_cart(request):
OrderItemFormSet = inlineformset_factory(Order, OrderItem, form=OrderItemForm, extra=0, can_delete=True)
order = Order.objects.get(pk=request.session['order'])
if request.method == 'GET':
formset = OrderItemFormSet(instance=order)
return render(request, 'ordersys/cart.html', {'formset': formset})
elif request.method == 'POST':
filled_formset = OrderItemFormSet(request.POST, instance=order)
if filled_formset.is_valid():
filled_formset.save()
return redirect('catalog:index')
else:
return render(request, 'ordersys/cart.html', {'formset': filled_formset})
cart.html
<form action="" method="post">
{{ formset.management_form }}
{% for form in formset %}
{{ form.id }}
{{ form.DELETE|add_class:"not_displayed" }} # custom filter
<img src="{{ form.instance.spec_prod.product.picture.url }}">
{{ form.quantity.label_tag }}
{{ form.quantity }}
{{ form.errors }}
<button type="button">Delete</button>
{% endfor %}
<button type="submit">Submit</button>
</form>
Next, if user press 'DELETE' button, my JavaScript
1. hides form with $(item).css('display', 'none');
2. makes checked form.DELETE checkbox with ItemDelCheckbox.prop('checked', true);
3. sends ajax request to delete item from database (otherwise if user refreshes the page, the item still in the cart)
views.py
def delete_order_item(request): # meets the ajax request
item_id = int(request.POST['item_id'])
order = get_object_or_404(Order, pk=int(request.POST['order_id']))
order.remove_item(item_id)
if order.is_empty(): # if the last item is deleted
order.untie(request.session)
order.delete()
return HttpResponse()

Instead of manually creating the hidden field using {{ form.DELETE }}, you can probably use 'can_delete' while instantiating the formset which does the same. For eg,
ArticleFormSet = formset_factory(ArticleForm, can_delete=True)
Refer can_delete

Related

Django unable delete form with data shown

i have created a delete function for my forms but it can only delete if i did not display out my information.
However if i try to display the information out on the delete page, the delete function wouldn't work.
i think because when i display the information out, the form isn't valid, hence unable to delete the object.
My views.py
def servicing_entry_delete(request, serv_entry_id):
delete_object = get_object_or_404(serv_entry, id=serv_entry_id)
if request.method == 'POST':
form = Delete_Flying_form(request.POST, instance=delete_object)
if form.is_valid(): # checks CSRF
delete_object.delete()
return HttpResponseRedirect(reversed('hello:servicing_entry')) # wherever to go after deleting
else:
form = Delete_Flying_form(instance=delete_object)
return render(request, 'hello/servicing_entry_delete.html', {'form':form})
My forms.py
class Delete_Flying_form(forms.ModelForm):
class Meta:
model = serv_entry
fields = ('Servicing_Type', 'Time', 'Date','Ic_Clear_Name','Ic_Clear_Time','Ic_Clear_Date')
My deleteconfirmation.html
{% extends 'hello/base.html' %}
{% block content %}
<h1>Delete Servicing Entry</h1>
<p>Are you sure you want to delete this Entry: </p>
{{ form }}
<form action="" method="POST">
{% csrf_token %}
<input type="submit" action="" value="Yes, delete this Entry." />
</form>
{% endblock %}
Without the {{ form }}, i am able to delete the object smoothly but not being able to display any information.
However if i try to display, like this
is there anyway if i could display this few fields and still delete successfully?
My django version is 1.11
with python 2.7.5
Much appreciate!

to retrieve values of multiple radio buttons and form fields in views.py django

i want to receive the radio button values for the table generated within the form multiple times in views.py in django.
My templates file is something like :
<form action = "/appName/accept" method = "POST"
{% for l in list %}
<table>
<tr>
<td>{{l.value}}</td>
</>tr
</table>
<input type = "radio" name = "acceptance" value ="accept">Accept</input>
<input type = "radio" name = "acceptance" value ="reject">Reject</input>
{% endfor %}
<input type ="submit" name = "submit">SUBMIT</input>
</form>
that means rendered page will look like :
value 1
Accept
Reject
value 2
Accept
Reject
and so on
SUBMIT BUTTON
as soon as user clicks the submit button, I want to collect the values of all the radio buttons for each value as well as the table rows but how to do that in views.py? I know that if we give common name to radio input like here "acceptance", so only of one of them will be selected at a time. Please Help. I am new to this concept and django.
you should better use django formsets https://docs.djangoproject.com/en/1.8/topics/forms/formsets/
As mentioned in documentation :
from django.forms.formsets import formset_factory
from django.shortcuts import render_to_response
from myapp.forms import ArticleForm
def manage_articles(request):
ArticleFormSet = formset_factory(ArticleForm)
if request.method == 'POST':
formset = ArticleFormSet(request.POST, request.FILES)
if formset.is_valid():
for form in formset:
form.cleaned_data...
# here you can access each radio button
else:
formset = ArticleFormSet()
return render_to_response('manage_articles.html', {'formset': formset})
The manage_articles.html template might look like this:
<form method="post" action="">
{{ formset.management_form }}
<table>
{% for form in formset %}
{{ form }}
{% endfor %}
</table>
</form>

A way to render content to a base template

I have a base template which contains header, footer and a block "content" which then I override in different CBVs.
There is little "user-space" divinside a header where I want to keep user's info in case he is logged in and a login form if he is a guest.
Right now the only way that comes in mind is to create a django app and use it as a tag. But I think there is a better solution. Because as far as I know tags can slow down django project in future.
Also I think that maybe I would like to manipulate this div from a view which renders a child template. For example I calculate and display some value on a page but also I want to display it in a "user-space" div as well. I think this can be achieved by pushing data to the user's session and then using this while rendering "user-space" div from another code.
Assuming you have django.contrib.auth.user in your INSTALLED_APPS, you can access the user's login status using user.is_authenticated():
{% if user.is_authenticated %}
<div>Welcome back, {{ user.username }} | <a href='/logout/'>Logout</a></div>
{% else %}
<div>
<form action='/login/' method='POST'>
{{ csrf_token }}
{{ login_form }}
<input type='submit'>
</form>
</div>
{% endif %}
Edit:
In response to your comment:
Let's suppose client does a POST request by which we calculate some number - total price. And I need to display it in that div.
As documented
Define your view:
from django.shortcuts import render
from .forms import MyForm
def simple_view(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = MyForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
result = form.cleaned_data['some_input'] * 50
return render(request, 'my_template.html', {'result': result})
# if a GET (or any other method) we'll create a blank form
else:
form = MyForm()
return render(request, 'my_other_template.html', {'form': form})
Display result in your template:
<!-- my_template.html -->
<div>{{ result }}</div>
Maybe you need to be more specific with your question, this is what I think you are looking for though. You should put logic like you are describing into a view, not a template.

How do I use the same view with two different forms and don’t loose the data provided by the user with Flask?

I have a form with several input fields about a meeting.
#app.route('/Alt_Reuniao/<WCodigoChaveP>', methods=['GET', 'POST'])
def Alt_Reuniao(WCodigoChaveP):
# This function returns a list with de data from the Database about a specific meeting
WReuniao = Executa_Query_Leitura("0005", WCodigoChaveP, "O")
if not WReuniao:
flash('Error.......')
return redirect(url_for('Cad_Reunioes'))
else:
if WReuniao[8]:
# this function returns a list with de data from the Database about the topic of the meeting
WAssunto = Executa_Query_Leitura("0002", WReuniao[8], "O")
Wform = Cad_Reunioes_Form()
if request.method == "POST":
if Wform.validate():
# save the data ......
return redirect(url_for('Cad_Reunioes'))
else:
for Werro in Wform.errors.values():
flash(Werro[0])
return render_template('Reuniao.html', Wformulario=Wform)
else:
Wform.WCPO_Reuniao.data = WReuniao[1]
Wform.WCPO_Desc_Reuniao.data = WReuniao[2]
Wform.WCPO_Nro_Part.data = WReuniao[3]
Wform.WCPO_Cod_Assunto.data = WReuniao[8]
if WReuniao[8]:
if WAssunto:
Wform.WCPO_Assunto.data = WAssunto[1]
return render_template('Reuniao.html', Wformulario=Wform)
This is my Reuniao.html template:
{% extends "Base_Cad_2.html" %}
{% block dados %}
{{ Wformulario.WCPO_Reuniao.label(id="WCPO_Reuniao", class="lab1") }} {{ Wformulario.WCPO_Reuniao(size = 100, maxlength=30, id="WCPO_Reuniao") }}
<p id="PL"> {{ Wformulario.WCPO_L_Desc_Reuniao(id="WCPO_L_Desc_Reuniao", class="lab1") }} </p>
{{ Wformulario.WCPO_Desc_Reuniao(rows=5, cols=100, id="WCPO_Desc_Reuniao") }}
{{ Wformulario.WCPO_Nro_Part.label(id="WCPO_Nro_Part", class="lab1") }} {{ Wformulario.WCPO_Nro_Part(size = 5, id="WCPO_Nro_Part") }}
{{ Wformulario.WCPO_Cod_Assunto.label(id="WCPO_Cod_Assunto", class="lab1") }} {{ Wformulario.WCPO_Cod_Assunto(size=10, readonly='readonly', id="WCPO_Cod_Assunto") }}
{{ Wformulario.WCPO_Assunto.label(id="WCPO_Assunto", class="lab1") }} {{ Wformulario.WCPO_Assunto(size=95, readonly='readonly', id="WCPO_Assunto") }}
<button id="Selec_Assunto" name="Selec_Assunto" value="Selec_Assunto" type="button"> <a class="botoes" href="{{ url_for('Selec_Assuntos_Inicio', WRotChama = "001", WCodorig = Wformulario.WCPO_Cod_Reuniao ) }}" hreflang="pt-br"> Seleciona </a> </button>
{% endblock %}
{% block botoes %}
<button id="gravar" name="gravar" value="Gravar" type="submit" class="botoes" > Gravar </button>
{% endblock %}
Basically, this view works fine.
When I select a meeting from a list in a previous template the view method is a GET and the data from the database is passed to the form and the template renders correctly.
When the method is a POST the data from the form is saved correctly in the Database,…
On the form there is a button Selec_Assunto. When the user click on that button I point to a view witch renders a template with a list of all possible topics for the meeting. These topics come from the database. There can be a lot of them so I can’t just use a combo. That’s why I use a template.
When the user select an topic from the list I have to render Alt_Reuniao view again and I have to pass to the view the selected topic.
This is working fine.
My problem is this: the method again is a GET. If before hitting the Selec_Assunto button the user alter or input data on the other fields in the form I loose these data from the user when the topic is selected. The view render the template with the data from the database again.
Everything seems to be working fine. I just want to maintain the data the user already change in the form before clicking on the Selec_Assunto button.
As you can see I’m new in Web developing, Python, Flask,…
Thanks a lot in advance for your help.
In this case you could update the "Selec_Assunto" button behavior to post the form data back to the server, but also include a redirect variable. When the redirect variable is included, the server would save the form changes and then redirect to the "Selec_Assuntos_Inicio" view, whereas it would follow the previous/normal form posting behavior if the redirect variable isn't present. For example:
if request.method == "POST":
if Wform.validate():
# save the data ……
if Wform.redirect.data:
return redirect(Wform.redirect.data)
else:
return redirect(url_for('Cad_Reunioes'))
else:
for Werro in Wform.errors.values():
flash(Werro[0])
return render_template('Reuniao.html', Wformulario=Wform)
It's worth noting that this approach requires you to use JavaScript to override the "Selec_Assunto" button behavior (as you'd be forcing it to perform a form submission essentially). Here's one way you could do that using jQuery:
$('button#Selec_Assunto').on('click', function() {
$('form#formId').append('<input type="hidden" name="redirect" value="{{ url_for('Selec_Assuntos_Inicio', WRotChama = "001", WCodorig = Wformulario.WCPO_Cod_Reuniao ) }}">');
$('form#formId').submit();
});
That said, a potentially better option from both a coding and usability perspective would be to asynchronously load the topic data into the existing page so that you don't have to reload the view at all. That would enable you to avoid needing to do an interim save of the form data.

Django edit template for ModelForm is not being filled in with values

I have a ModelForm that I have created a view and template with to add an instance to the database. I am now trying to extend this to be editable when the user clicks a button - but when they do that the form/template page appears and I get the "This Field is required message" but all fields are empty instead of pre-populated with the instance I passed in, but when I edit the values to something different than originally then in my database the correct instance is updated. So it is passing the primary key but none of the values are showing up. If anyone can tell what I am doing wrong I would appreciate it, oh, and I am using this post Django edit form based on add form? as a basis so please don't just send me there.
Here are my files
ModelForm
class CreditCardForm(forms.ModelForm):
class Meta:
model = CreditCard
fields = ('name_on_card','card_number','contact_number_on_card')
View
def edit(request, id=None, template_name='article_edit_template.html'):
if id:
print "Edit Mode"
card = get_object_or_404(CreditCard, pk=id)
if card.card_owner != request.user:
raise HttpResponseForbidden()
else:
print "Create Mode"
card = CreditCard(card_owner=request.user)
if request.POST:
print "request.POST"
form = CreditCardForm(request.POST, instance=card)
if form.is_valid():
print "is_valid()"
form.save()
# If the save was successful, redirect to another page
# redirect_url = reverse(article_save_success)
return HttpResponseRedirect('/cards/')
else:
print "else"
form = CreditCardForm(instance=card)
return render_to_response(
'create_credit.html',
{'form': form,},
context_instance=RequestContext(request)
)
Template
{% include "base.html" %}
<form action="" method="post">{% csrf_token %}
<fieldset>
<legend>Required</legend>
<div class="fieldWrapper">
{{ form.name_on_card.errors }}
<label for="id_topic">Name as it appears on card:</label>
{{ form.name_on_card }}
</div>
<div class="fieldWrapper">
{{ form.card_number.errors }}
<label for="id_topic">Last 6 digits of card number:</label>
{{ form.card_number }}
</div>
</fieldset>
<fieldset>
<legend>Optional</legend>
<!-- This is for Credit Card's Only -->
<div class="fieldWrapper">
{{ form.contact_number_on_card.errors }}
<label for="id_topic">Institution contact number: 1-</label>
{{ form.contact_number_on_card }}
</div>
</fieldset>
<p><input type="submit" value="Save"></p>
</form>
URLS
url(r'^new/credit/$', views.edit, {}, 'crds_newCredit'),
url(r'^edit/credit/(?P<id>\d+)/$', views.edit, {}, 'crds_editCredit'),
#xizor use a link instead a button to redirect to the edition form, becasue with the button you are probably sending a post to the view.
PS: Please forgive my English, edit this answer if you think it is right to make it more useful.

Categories