Django only show first POST element in loop - django

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.

Related

HTMX form submission produces a duplicate form

{% 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.

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 }}"}'>

Reverse for 'edit' with no arguments not found. 1 pattern(s) tried: ['articles/edit/(?P<pk>[0-9]+)/$']

I am a beginner in Django and now i am developing a blogging application. At the article editing section i got stucked and I dont know why its showing this error. Searched a lot and cant find an answer
NoReverseMatch at /articles/edit/2/
Reverse for 'edit' with no arguments not found. 1 pattern(s) tried: ['articles/edit/(?P<pk>[0-9]+)/$']
edit_articles section in
views.py
#login_required(login_url="/accounts/login/")
def edit_articles(request, pk):
article = get_object_or_404(Article, id=pk)
if request.method == 'POST':
form = forms.CreateArticle(request.POST, instance=article)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect('articles:myarticles')
else:
form = forms.CreateArticle(instance=article)
return render(request, 'articles/article_edit.html', {'form': form})
article_edit.html
{% extends 'base_layout.html' %}
{% block content %}
<div class="create-article">
<form class="site-form" action="{% url 'articles:edit' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="jumbotron">
<div class="heading col-md-12 text-center">
<h1 class="b-heading">Edit Article</h1>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
{{ form.title }}
</div>
</div>
<div class="col-md-6">
<div class="form-group">
{{ form.slug }}
</div>
</div>
<div class="col-md-6">
<div class="form-group">
{{ form.thumb }}
</div>
</div>
<div class="col-md-12">
<div class="form-group">
{{ form.body }}
</div>
</div>
</div>
<button type="submit" class="btn btn-primary btn-lg btn-block">Update</button>
</div>
</form>
</div>
{% endblock %}
urls.py
from django.urls import path
from . import views
app_name = 'articles'
urlpatterns = [
path('', views.article_list, name='list'),
path('create/', views.article_create, name='create'),
path('edit/<int:pk>/', views.edit_articles, name='edit'),
path('myarticles/',views.my_articles, name='myarticles'),
path('<slug>/', views.article_detail, name='details'),
]
my_articles.html
That button in 4th is triggering the edit function with primary key and redirects to edit page
<tbody>
{% if articles %}
{% for article in articles %}
<tr class="table-active">
<th scope="row">1</th>
<td>{{ article.title }}</td>
<td>{{ article.date }}</td>
<td><button type="button" class="btn btn-info">Edit</button></td>
{% endfor %}
</tr>
{% else %}
<tr>
<td colspan=4 class="text-center text-danger">Oops!! You dont have any articles.</td>
</tr>
{% endif %}
In your template article_edit.html - post url is expecting a pk, you need to pass a pk just like you are doing it for template my_articles.html
<div class="create-article"><form class="site-form" action="{% url 'articles:edit' pk=form.instance.pk %}" method="post" enctype="multipart/form-data">{% csrf_token %}...
This way django knows which article you are editing

Django post duplicates on page refresh

I have this view which adds replies to a topic:
#login_required
def post_reply(request, topic_id):
tform = PostForm()
topic = Topic.objects.get(pk=topic_id)
args = {}
if request.method == 'POST':
post = PostForm(request.POST)
if post.is_valid():
p = post.save(commit = False)
p.topic = topic
p.title = post.cleaned_data['title']
p.body = post.cleaned_data['body']
p.creator = request.user
p.user_ip = request.META['REMOTE_ADDR']
p.save()
tid = int(topic_id)
args['topic_id'] = tid
args['topic'] = topic
args['posts'] = Post.objects.filter(topic_id= topic_id).order_by('created')
return render_to_response("myforum/topic.html",args)
else:
args.update(csrf(request))
args['form'] = tform
args['topic'] = topic
return render_to_response('myforum/reply.html', args,
context_instance=RequestContext(request))
The problem is that when user refershes the page after posting a reply her reply is being duplicated. How to avoid this?
UPDATE:Here is the related template:
{% extends "base.html"%}
{% load static %}
{% block content%}
<div class="panel">
<div class="container">
<!-- Posts -->
<div class="col-md-12">
<h3>{{ topic.title }}</h3>
<table class="table table-striped">
<tr class="col-md-9"><td>{{ topic.description }}</td></tr>
<tr class="col-md-3"><div class="userpanel"><td>{{ topic.created }}</td></div></tr>
{% for post in posts %}
<tr class="col-md-12 userpanel"><td>{{ post.title }}</td></tr>
<tr class="">
<td>
<div class="col-md-9 userpanel">{{ post.body }} <br> {{ post.created }} </div>
</td>
<td>
<div class="col-md-3 userpanel">{{ post.creator }} <br> {{ post.created }} </div>
</td>
</tr>
{% endfor %}
</table>
<hr />
<!-- Next/Prev page links -->
{% if posts.object_list and posts.paginator.num_pages > 1 %}
<div class="pagination">
<span class="step-links">
{% if posts.has_previous %}
previous <<
{% endif %}
<span class="current">
Page {{ posts.number }} of {{ topics.paginator.num_pages }}
</span>
{% if posts.has_next %}
>> next
{% endif %}
</span>
</div>
{% endif %}
<a class="button" href="/forum/reply/{{topic.id}}"> Reply </a>
</div> <!-- End col-md-12 -->
</div> <!-- End container -->
</div>
{% endblock %}
Always - I repeat, always, redirect after a POST.
So instead of doing a return render_to_response("myforum/topic.html",args) when your form is valid, do a return HttpResponseRedirect(url_of_your_view) (https://docs.djangoproject.com/en/1.7/ref/request-response/#django.http.HttpResponseRedirect)
Update after OP's comments:
You should use reverse (https://docs.djangoproject.com/en/1.7/ref/urlresolvers/#reverse) to create the url to redirect to:
return HttpResponseRedirect(reverse('myforum.views.topic', args=[topic_id]))
Also, I recommend to name your views (and drop strings for defining them since they are deprecated), so change your urls.py line like this:
from myforum.views import topic
...
url(r'^topic/(\d+)/$', topic, name='view_topic'),
...
And then just do a reverse('view_topic', args=[topic_id]) to get the url of your view.

some of the error list members in django form doesn't show up in the template

I have a form in django and I wanna show errors before each field.
The problem is when I use form.field_name.errors or form.errors.field_name, it happens for one of the fields that error does not show up, just for one of them, here's the template code:
<table class="">
<form action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div>
<div>{{ form.errors.competitor_name }}</div>
<br/>
<div>name:</div>
<div>{{ form.competitor_name }}</div>
</div>
<div>
<div>{{ form.errors.notional_code }}</div>
<br/>
<div>code:</div>
<div>{{ form.national_code }}</div>
</div>
<div>
<table id="filesContainer">
<tbody>
{% for form_ in formset.forms %}
<tr id="{{ form_.prefix }}-row">
<td>{{ form_.file.label }}:</td>
<td>{{ form_.file }}</td>
<td></td>
</tr>
{% endfor %}
</tbody>
</table>
<p>
{{ formset.management_form }}
</p>
</div>
<div class="arsh-signup-row">
<input type="submit" value="SignUp" />
</div>
</form>
</table>
I have problem in showing errors of national_code field.
I have used break points and I absolutely realized that I'm adding errors in the right way and everything about form is alright, it seems something is wrong with the template and I don't know what's this.
The interesting part is, when I wanna show up this field's error in some other part of the page, it's ok, everything is done, but it doesn't show in that specific part, if I use this code:
<div>
<div>{{ form.errors.notional_code }}</div>
<br/>
<div>name:</div>
<div>{{ form.competitor_name }}</div>
</div>
<div>
<div>{{ form.errors.notional_code }}</div>
<br/>
<div>code:</div>
<div>{{ form.national_code }}</div>
</div>
I can see what I want. It's really funny at first, but now it's confusing for me.
Any advice would be appreciated.
You get errors in field_name.errors for each field.
form.errors gives any error for entire form and not specific to a field.
Refer below sample from django docs here
<form action="/contact/" method="post">
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }}: {{ field }}
</div>
{% endfor %}
<p><input type="submit" value="Send message" /></p>
the problem would be solved with this code lines:
{% for field in form %}
<div class="row">
<div class="error">{{ field.errors }}</div>
<br/>
<div class="lable">{{ field.label }}</div>
<div class="field">{{ field }}</div>
</div>
{% endfor %}
;-)