问题
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 ?
回答1:
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()
回答2:
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
回答3:
I have question. Why when the user refreshes the page the item is still in the cart?
来源:https://stackoverflow.com/questions/47954063/how-to-dynamically-delete-object-using-django-formset