How do I bulk_delete in django? - django-views

I need to delete select items by a checkbox and delete all the so selected items.
I tried this but it doesn't work.
I have tried other similar questions but couldn't get help
<table class="layout container">
<thead>
<th>Name</th>
<th>Mark</th>
</thead>
{% for book in books %}
<tr>
<td>{{ book.book_name }}</td>
<td>
<input type="checkbox" name="marked_delete" value="{{ book.pk }}" {% if '{{ book.pk }}' in 'marked_delete' %} checked {% endif %}>
</td>
</tr>
{% endfor %}
</table>
The view
def delete_bulk_books(request):
book = request.GET.get('marked_delete')
books = Books.objects.filter(id__in=marked_delete).delete()
return redirect("view_books")

You can use getlist(…) [Django-doc], instead of .get('marked_delete'):
def delete_bulk_books(request):
book = request.GET.getlist('marked_delete')
books = Books.objects.filter(id__in=marked_delete).delete()
return redirect('view_books')
Normally however requests that have delete are done through a POST or DELETE request, you thus might want to work with a form that makes a POST request.

Related

Django object update using htmx and SingleObjectMixin

I'm using htmx for the first time. I have a table where each cell is a grade object. Previous to htmx I made each cell a link to an UpdateView for the object. I am now trying to have the user modify the the object's score field directly in the table using htmx. I'm sure I'm doing several things wrong.
My page loads as expected and the table is displayed as expected. when I type in a cell, I get an error Forbidden 403. CSRF Verification Failed.
The purpose of this post/question is to figure out how to get past this 403 error. Having said that, if it turns out that I'm going down the completely wrong path with using a SingleObjectMixin, please let me know.
View
class GradeChange(SingleObjectMixin, View):
""" view to handle htmx grade change"""
model = Grade
def post(self, request, *args, **kwargs):
grade = self.get_object()
assessment = Assessment.objects.get(grade=grade.pk)
print(assessment)
classroom = Classroom.objects.get(classroom=grade.cblock)
print(classroom)
if grade.score == "EXT" or grade.score=="APP" or grade.score=="DEV" or grade.score=="BEG":
grade.save()
return HttpResponse("S")
else:
return HttpResponse("")
template
<table class="table table-bordered table-sm">
<thead>
<tr>
<th class="col-3" scope="col">Students</th>
{% for obj in objective_list %}
<th class="col-2" scope="col">{{ obj.objective_name }}</th>
{% endfor %}
<th scope="col">Comments</th>
</tr>
</thead>
<tbody>
<form action="" method="post" class="form-group">
{% csrf_token %}
{% for student in student_list %}
<tr>
<td >{{ student.student_first }} {{ student.student_last }}</td>
{% for g in grade_list %}
{% if g.student.id == student.id %}
<td>
<input type="text" hx-post="{% url 'gradebook:grade-change' g.pk %}" hx-target="" hx-trigger="keyup delay:2s" class="form-control score" title={{ g.score }} name="score" id="input-{{ forloop.counter0 }}" placeholder={{ g.score }} required>
</td>
{% endif %}
{% endfor %}
<td>
{% for comms in comment_list %}
{% if comms.student == student %}
<a class="grade-comment" href="{% url 'gradebook:addcomment' comms.pk assess_pk class_pk %}">{{ comms.comment|truncatewords:10 }}</a>
{% endif %}
{% endfor %}
</td>
</tr>
{% endfor %}
</form>
</tbody>
</table>
EDIT - a solution
I came across this, which works.
<script>
document.body.addEventListener('htmx:configRequest', (event) => {
event.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
});
</script>
The CSRF token must be included in the HTMX POST request. Currently you include it only in the regular form, but the HTMX request is initiated from a child input element where the token is not present. So just include the following hx-header attribute to a parent element like the <form> or event the <body> is a good candidate:
<body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>

Dynamically update table when creating new enty using HTMX

After recent success in some simple HTMX tasks I wanted to extend adamchainz django-htmx example by a modal field that updates a table dynamically. I am not sure if I am returning the right thing in render ... but my problem is, the table is not being updated. Only when I hit reload.
view:
class CreateProductView(TemplateView):
template_name = "app/product_create.html"
form_class = OrderForm
def get(self, request, *args, **kwargs):
return render(request, self.template_name, {'form': self.form_class()})
def post(self, request):
Product.objects.create(name = request.POST["name"], price = request.POST["price"])
part_template = "app/partial-rendering.html"
return render(request, part_template, {"base_template": "app/_partial.html"})
urls.py:
path("create/", views.CreateProductView.as_view(), name = 'create'),
This is my index.html with the table in it:
<div class="..."><button class="btn btn-primary"
id="showButton"
hx-get="/create"
hx-target="#modal-create">create</button</div>
<main role="main" id="main">{% include "app/orders_table.html" %}</main>
<div id="modal-create"></div>
I also have a partial-rendering.html in place:
{% extends base_template %}
{% block main %}
{% include "app/product_table.html" %}
{% endblock %}
and a _partial.html:
<main id="main">
{% block main %}{% endblock %}
</main>
I will not post the whole product_table.html here, I guess it is straight forward ... mainly:
<table class="table table-striped table-hover">
<thead>
<tr>
<th>product name</th>
<th>price</th>
</tr>
</thead>
<tbody>
{% for product in page.object_list %}
<tr>
<td>{{ product.name}}</td>
<td>{{ product.price }}</td>
</tr>
{% endfor %}
</tbody>
</table>
The data from the form is collected via JS and sent to django using AJAX. I did not use the normal form submit because I wanted to avoid a page reload (which would solve that problem, I know).
I did try to put this together from the example mentioned above. Everything except the dynamic page update runs well!
Some approach is to remove the div targeted:
<div id="modal-create"></div>
then in your table body write the id deleted:
<tbody id="modal-create">
you want some partial for your table loops
{% for product in page.object_list %}
{% include 'some_table_body_tr_partial.html' %}
{% endfor %}
your partial might contain something like this:
<tr hx-target="this" hx-swap="outerHTML">
<td>{{ product.name}}</td>
<td>{{ product.price }}</td>
</tr>
This approach have an issue: the width of the form will take the wide of the first column.

How to resolve "jinja2.exceptions.UndefinedError: 'nutrition' is undefined" error in Flask for CS50 Final Project

I am getting following error when i run the flask: jinja2.exceptions.UndefinedError: 'nutrition' is undefined. I tried several things but still couldn't resolve it.
Please help resolve the problem.
Code is below:
#app.route("/", methods=["GET", "POST"])
def index():
if request.method == "POST":
nutrition = Recipe_nutrition(request.form.get("recipes"))
instructions = Instructions(request.form.get("recipes"))
return render_template("index.html", nutrition = nutrition, instructions = instructions)
# User reached route via GET (as by clicking a link or via redi)
else:
return render_template("index.html")
{% extends "layout.html" %}
{% block title %}
Index
{% endblock %}
{% block main %}
<form action="/" method="post">
<div class="form-group">
<input autocomplete="off" autofocus class="form-control" name="recipes" placeholder="Recipe" type="text" required/>
</div>
<button class="btn btn-primary" type="submit">Search</button>
</form>
<table class="table table-striped" style="width:100%">
<tr>
<th>Nutrition</th>
<th>Amount</th>
<th>Units</th>
</tr>
{% for keys, values in nutrition.items() %}
<tr>
<td>{{ keys }}</td>
<td>{{ values[0] }}</td>
<td>{{ values[1] }}</td>
</tr>
{% endfor %}
</table>
<table class="table table-striped" style="width:100%">
<tr>
<th>#</th>
<th>Steps</th>
</tr>
{% for keys, values in instructions.items() %}
<tr>
<td>{{ keys }}</td>
<td>{{ values }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
In your Jinja template, you both access nutrition and instructions, but you only set them in case of a POST request.
You have several options now.
Either set both to an empty list, and also pass them into your render_template function for the GET case (your elsebranch).
Or return a different template, where nutrition and instructions are not necessary.
Maybe there is also a builtin Jinja solution, but I do not know one.

Delete multiple rows in django

I am trying to delete severals rows at the same time in django.
How can I simply change the state of the booleanfield 'to_delete', just by clicking on the checkbox?
I am using datatables. The way how I am doing it is to create a boolean to_delete in my model, when the checkbox is selected, I am calling the function delete_multiple_company in my view. However, this doesn`t work. Any idea, what I am doing wrong please. Many Thanks,
I`ve created my view:
views.py
def delete_multiple_company(request, company_id):
company = get_object_or_404(Company, pk=company_id)
company = Company.objects.get(pk=company_id)
company.objects.filter(to_delete=True).delete()
return HttpResponseRedirect(reverse("company:index_company"))
urls.py
url(r'^(?P<company_id>[0-9]+)/delete_multiple_company/$', views.delete_multiple_company, name='delete_multiple_company'),
models.py
class Company(models.Model):
to_delete = models.BooleanField(default=False)
index.html
<span class="fa fa-plus"></span>Delete Companies
<table id="dtBasicExample" class="table table-striped table-hover">
<thead>
<tr>
<th>Select</th>
<th>#</th>
<th>Checked ?</th>
</tr>
</thead>
<tbody>
{% for company in companys.all %}
<tr>
<td id="{{ company.id }}"><input type="checkbox" class="companyCheckbox" name="checkedbox" id="{{ company.id }}" value="{{ company.id }}"></td>
<td>{{ company.id }}</td>
<td>{{ company.to_delete }}</td>
</tr>
{% endfor %}
</tbody>
</table>
I experienced a similar issue as you, what I did was put in a form.
index.py
<form method="POST" class="post-form">{% csrf_token %}
<button type="submit" class="save btn btn-default">Delete</button>
<span class="fa fa-plus"></span>Delete Companies</a>
<table id="dtBasicExample" class="table table-striped table-hover">
<thead>
<tr>
<th>Select</th>
<th>#</th>
<th>Checked ?</th>
</tr>
</thead>
<tbody>
{% for company in companys.all %}
<tr>
<td id="{{ company.id }}"><input type="checkbox" class="companyCheckbox" name="checkedbox" id="{{ company.id }}" value="{{ company.id }}"></td>
<td>{{ company.id }}</td>
<td>{{ company.to_delete }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<button type="submit" class="save btn btn-default">Delete</button>
</form>
Clicking on the button then triggered the POST method in my view.
views.py
def index(request):
if request.method == 'POST':
print('I made it here')
# Put your code here, note you will return a dict, so some trial and error should be expected

Django model filter not pulling in object

I'm using Django 1.4 with Python 2.7 on Ubuntu 12.04.
I have a template that is supposed to show a product and a list of product features for each product and for some reason the features don't show in the template.
Here is the view:
#login_required
def view_products(request):
"""
.. function:: view_products()
View the Products
:param request: Django Request object
"""
data = { 'user' : request.user }
if (request.user.is_authenticated() and request.user.is_superuser):
products = Products.objects.all()
add_feature_form = rsb.forms.AddProductFeatureForm();
data.update({ 'form' : add_feature_form })
data.update({ 'products' : products })
data.update(csrf(request))
return render_to_response("view_products.html", data)
return render_to_response("index.html", data)
Here is the portion of the template working with the product features:
<table>
{% for product in products %}
<tr>
<td align="right">Product Name:</td><td>{{ product.name }}</td>
</tr>
<tr>
<td align="right">Price:<br /></td><td>${{ product.price }}</td>
</tr>
<tr>
<ul>
{% for productfeature in product.productfeature_set.all %}
<form action="/removeProductFeature/" method="post">{% csrf_token %}
<li>
{{ productfeature.feature }}
<input type="hidden" name="feature" value={{ productfeature.feature }}>
<input type="hidden" name="product_id" value={{ product.id }}>
<label class="formlabel"> </label><input type="submit" value="Remove ►">
</tr>
</form>
{% endfor %}
</ul>
</tr>
<tr>
<form action="/addProductFeature/" method="post">{% csrf_token %}
<table>
<tr>
<td align="right"><label class="formlabel">Add Feature:<br /></label></td><td>{{ form.feature }}</td>
</tr>
<input type="hidden" name="product_id" value={{ product.id }}>
<tr>
<td align="right"><label class="formlabel"> </label></td><td><input type="submit" value="Add ►"></td>
</tr>
</form>
</table>
</tr>
{% endfor %}
</table>
Basically this template should show you a product. Each feature will be listed below it with the option to "remove" that feature. Then, at the bottom, a field that allows you to add additional features.
The existing features do not show up at all. Any suggestions on what I might be doing wrong?
UPDATE 1:
I missed an s in the template. product.productfeatures_set.all not product.productfeature_set.all. I'm good to go. Thanks all!
Please don't do this:
product_features = []
for product in products:
features = ProductFeatures.objects.filter(product = product)
product_features.append(features)
product.features = product_features
Instead, just pass your products variable to the template context.
And in the template do:
{% for product in products %}
Product id: {{ product.pk }}
{% for productfeature in product.productfeature_set.all %}
{{ productfeature.feature }}
{% endfor %}
{% endfor %}
What is productfeature_set you would ask (or, I hope you would ask :D), and that's a very good question. Don't panic, it's all documented.
Now, this is going to cause subqueries to spawn. The solution is to use prefetch_related. But you don't have to worry about that for the moment I think :)
Yes, you are trying to implement something that is already build into Django!
You should use the build in reverse relations of django.
# give an Product instance product
# this should work
product.productfeature_set.all()
Which is accessible from the template a product.productfeature_set.all
and can be itterated over.