With Django 1.8 template, tried to POST table data. I set <TABLE> id = 'tasktable'. But in request information, there's no data of 'tasktable'
Also, how can I access dynamic table POST data in Django views?
Is only ways to make id each row?
(like <td><input id='1_data'></td><td><input id='2_data'></td>
POST request info
Below Code is template.
{% extends "base.html" %}
{% load staticfiles %}
{% block scripts %}
<script src="//code.jquery.com/jquery.min.js"></script>
<script>
$(function () {
$('#btn-add-row').click( function() {
$('#tasktable > tbody:last').append('<tr><td><input type="text"></td><td><select name="format"><option value="INT">INT</option><option value="TEXT">TEXT</option></select></td></tr>');
});
$('#btn-delete-row').click( function() {
$('#tasktable > tbody:last > tr:last').remove();
});
});
</script>
{% endblock %}
{% block title %}
Angel Feeder - Manager
{% endblock %}
{% block body %}
<div class="container">
<form class="" method="post" action="">
{% csrf_token %}
{{ taskform }}
<br>
<button id="btn-add-row" type="button">+</button><button id="btn-delete-row" type="button">-</button>
<table name='tasktable' id='tasktable' class="table">
<thead>
<tr>
<th>Data name</th>
<th>Format</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="text"></td>
<td><select name="format"><option value="INT">INT</option><option value="TEXT">TEXT</option></select></td>
</tr>
</tbody>
</table>
<button class="btn btn-lg btn-primary" type="submit">set task</button>
</form>
</div>
{% endblock %}
Only form elements with a name attribute will have their values passed when submitting a form.
name is not an allowed attribute of <table> in HTML5
https://stackoverflow.com/a/13677670/3033586
Obviously you have to set unique names for each form element inside table>tr>td so you can reference it after post
If you want simple styled [model]forms (for example with bootstrap) - look at http://django-crispy-forms.readthedocs.org/en/latest/
Related
{% extends "IntakeApp/base3.html" %}
{% load static %}
{% load crispy_forms_tags %}
{% block heading %}
<h2>Allergies for {{request.session.report_claimant}}</h2>
{% endblock %}
{% block content %}
<form hx-post="{% url 'allergy' %}" hx-target="#allergy_target" hx-swap="outerHTML">{% csrf_token %}
<div class="form-row">
<div class="form-group col-md-2 mb-0">
{{ form.allergen|as_crispy_field }}
</div>
</div>
<button type="submit" class="btn btn-primary">Add</button>
</form>
<div class="container-fluid">
<table class="table table-striped table-sm" id="med-table">
<thead>
<tr>
<th>Allergen</th>
</tr>
</thead>
<tbody id="allergy_target">
{% for allergy in allergy_list %}
<tr>
<td>{{allergy.allergen}}</td>
<td>
<form method="POST" action="{% url 'allergy-delete' allergy.id %}">{% csrf_token %}
<input class="btn btn-danger btn-sm" type="submit" value="Delete">
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
class AllergyCreateView(generic.CreateView):
model = Allergy
template_name = 'IntakeApp/allergy_form.html'
form_class = AllergyForm
def form_valid(self, form):
form.instance.assessment = Assessment.objects.get(id=self.request.session['assessment_id'])
return super(AllergyCreateView, self).form_valid(form)
def get_success_url(self):
return reverse_lazy("allergy")
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
assessment_id = self.request.session['assessment_id']
allergy_list = Allergy.objects.filter(assessment=assessment_id)
context["allergy_list"] = allergy_list
return context
I tried to all the different hx-swap options, but none fix it...It does post correctly and the swap does work, just not sure why I am getting another form in there. Please help
I added the View above. I think thats were my issue is...not sure if I am supposed to be doing this way or not?
Seems like you're trying to render the same html content in your view, which instead should only take a specific element.
You can try to use hx-select="#allergy_target" so htmx will only fetch the table content.
I have a webapp that renders a table of content. Content to be rendered in returned from a backend and Jinja2 is used to cycle through the data to put into appropriate columns and rows. This part works exactly as I expect. A web user performs some operation with data at each row using some buttons on each row.
In case the backend needs to display messages, I use flash(), with the message(s) rendered at the end of the table. Again, as constructed this works as I expect.
However, as the number of rows of the table grow, the place where messages are flash()'d wind up scrolling off the screen. So I was wondering if I could render flash()'d messages at each row. The intent is that the user receives messages as needed directly under each "affected" row.
The main challenge is to hide the message rows until a message needs to be rendered, and I am not 100% sure how best to do that.
An alternative I was considering is to not use flash(), but deliver additional content as needed for "message" rows. I guess I still have the challenge of checking if anything needs to be rendered at a particular row, and how best to do that...
Here's my current HTML:
<table class="styled-table">
<thead>
<tr>
<th>
Filename
</th>
<th width="70%">
Content
</th>
<th>
id
</th>
<th></th> <!-- edit file button -->
<th></th> <!-- delete file button -->
</tr>
</thead>
<tbody>
{% for content in filesContent %}
<tr>
<td>
<!-- file name -->
<div>
{{ content[2] }}
</div>
</td>
<td>
<!-- file content -->
{{ content[3] }}
</div>
</td>
<td>
<div>
{{ content[1] }}
</div>
</td>
<td>
<td>
<!-- edit button -->
<div>
<a id="{{ content[2] }}" href="/editFile/{{ content[2] }}"><img
src="{{ url_for('static', filename='images/edit_small.png') }}"
alt="edit"></a>
</div>
</td>
<td>
<!-- delete button -->
<div>
<a id="{{ content[2] }}" href="/deleteFile/{{ content[2] }}"><img
src="{{ url_for('static', filename='images/trash_small.png') }}"
alt="delete"></a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</form>
<!--
The backend script returns messages using flash().
-->
<div class="fromBackEnd">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for message in messages %}
{% if "Error" not in message: %}
<div class="alert alert-info">
{{ message[1] }}
</div>
{% endif %}
{% if "Error" in message: %}
<div class="alert alert-warning">
{{ message[1] }}
</div>
{% endif %}
{% endfor %}
{% endif %}
{% endwith %}
</div>
What I'd like to be to do is something like (HTML simplified for brevity):
<table>
{% for content in filesContent %}
<tr>
... my current td content ...
<tr>
<!-- NEW MESSAGE ROWS TO BE DISPLAYED ONLY IF 'messageContent' not null -->
<tr id="<SOMEUNIQUEID>" style="display:none">
<!-- span across all columns -->
<td>
{{ messageContent }}
</td>
</tr>
{% endfor %}
</table>
This way, messages are rendered right under the "affected" row, and easier for users to see. I could use some javascript to change the display style, but maybe I could just use {% if... %} around the tr??
Any guidance would be greatly appreciated!!!
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 }}"}'>
I have simple library page that user can register, add and edit\delete records from library.
Template.html
{% extends "book/base.html" %}
{% block content %}
<div class="row justify-content-center mt-5">
</div>
{% if books %}
{% for book in books %}
<div class="table-users">
<div class="header"> {{ book.title }} </a> </div>
<table cellspacing="0">
<tr><center>
<th>Name</th>
<th>Author</th>
<th>Delete</th>
</center>
</tr>
<td> {{ book.name }} </td>
<td> {{ book.author }} </td>
<td> <form class="delete" method="POST" action="{% url 'deletebook' book.id %}"> {% csrf_token %} <button class="delete" type="submit" class="btn btn-warning">Delete</button></td>
views.py:
def deletebook (request, book_pk):
book = get_object_or_404(Book, pk=book_pk, user=request.user)
if request.method == 'POST':
book.delete()
return redirect('currentbooks')
With this loop only first element of POST method is active and working. Actually I'd debuggging html after rendered and i've seen that theres a only first element has a POST method. I searched on google and on stackoverflow and found something about the change id to class. But my template doesnt have any id. I also tried to move {% csrf_token %} outside of the loop but it doesnt work either. I think so, i missed something important here. I really appreciate if someone could help me out. Thank you in advance.
I have this piece of code :
{% extends 'base.html' %}
{% load static %}
{% block page_title %}Manage Staff{% endblock page_title %}
{% block content %}
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">Manage All Staff</h3>
</div>
<!-- /.card-header -->
<div class="card-body">
<table id="example2" class="table table-bordered table-hover">
<thead>
<tr>
<th>SN</th>
<th>Full Name</th>
<th>Email</th>
<th>Course</th>
</tr>
</thead>
<tbody>
{% for staff in allStaff %}
<tr>
<td>-</td>
<td>{{staff.last_name}}, {{staff.first_name}}</td>
<td>{{staff.email}}</td>
<td>{{staff.course.name}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</section>
{% endblock content %}
The block page_title is used to display the current page in base.html.
My question is, how can I avoid repeating the text "Manage Staff" ?
Is there a way to display the block page_title in this current file ?
What I earlier tried was passing the value from my views.py file, so I wanna be sure I am doing the right thing