Django - htmx 的第二个触发器尝试加载意外的 URL 给出 403 错误
Django - second trigger of htmx tries to load an unexpected URL gives a 403 error
我有 table 个单元格,每个单元格都有不同的对象实例,并使用 htmx 更新对象。我创建了一个 CBV,它从 htmx 中获取 request.post 并将修改后的对象保存到数据库中。 htmx 执行 hx-swap 并将新的 <input>
标记加载到我的表单中,以及一些基于保存的对象的背景颜色样式。我可以点击许多单元格并以这种方式更新多个对象。这按预期工作,我没有看到任何错误。
但是,我第二次尝试更新同一个 cell/object 时,出现了 403 错误。此错误不会显示在浏览器中。细胞似乎没有反应。我可以继续更新其他单元格。该错误出现在我的浏览器控制台中。
观看次数
asessmentdetail - 加载模板并检索所有对象以填充 table
的视图
def assessmentdetail(request, assess_pk, class_pk):
"""List the current grades for the assessment"""
user = request.user
assessment = Assessment.objects.get(id=assess_pk)
classblock = Classroom.objects.get(id=class_pk)
course_pk = classblock.course.pk
objective_list = Objective.objects.all().filter(
assessment=assess_pk).order_by('objective_name')
student_list = Student.objects.all().filter(
classroom=class_pk).order_by('nickname')
gra = Grade.objects.filter(
assessment=assess_pk).filter(cblock=classblock.id)
context = {'objective_list': objective_list}
new_or_update = gra.exists()
grade_report = [str(new_or_update)]
grade_list = []
grade_id_array = []
grade_report.append(len(objective_list))
# print(len(objective_list))
comment_list = []
n = 0
if gra:
for student in student_list:
# grade_report.append(student.nickname)
comms = AssessComment.objects.filter(
student=student, assessment=assessment).first()
if comms:
comment_list.append(comms)
else:
new_c = AssessComment(user=user,
student=student, assessment=assessment, comment="---")
new_c.save()
comment_list.append(new_c)
for obj in objective_list:
if gra.filter(objective=obj, student=student.id).last():
grade_report.append(q.score)
grade_id_array.append(q.id)
grade_list.append(q)
n = n + 1
context['grade_report'] = grade_report
context['grade_list'] = grade_list
context['grade_id_array'] = grade_id_array
context['student_list'] = student_list
context['assessment'] = assessment
context['class_pk'] = class_pk
context['assess_pk'] = assess_pk
context['course_pk'] = course_pk
context['comment_list'] = comment_list
context['classblock'] = classblock
)
return render(request, "gradebook/assessment_detail.html", context)
GradeChange - 通过htmx修改对象的视图
class GradeChange(SingleObjectMixin, View):
""" view to handle htmx grade change"""
model = Grade
def post(self, request, *args, **kwargs):
grade = self.get_object()
ns = request.POST.get('score')
new_score = ns.upper()
def get_color(grade): # map background color to the score
if grade == "EXT":
convert_code = "rgba(153,102,255,0.4)"
elif grade == "APP+" or grade == "PRF+":
convert_code = "rgba(75, 192, 192, 0.7)"
elif grade == "APP" or grade == "PRF":
convert_code = "rgba(75, 192, 192, 0.3)"
elif grade == "DEV":
convert_code = "rgba(255, 205, 86, 0.4)"
elif grade == "EMG" or grade == "BEG":
convert_code = "rgba(225, 99, 132, 0.4)"
else:
convert_code = "rgba(0, 0, 0, 0.1)"
return (convert_code)
score_list = ["EXT", "APP+", "PRF+", "APP", "PRF", "DEV", "BEG", "EMG", "I", "---"]
if new_score in score_list:
grade.score = new_score
grade.save()
grade_score=str(grade.score)
bgcode = get_color(grade.score)
input_string=f'<input type="text" hx-post="{{% url "gradebook:grade-change" { grade.pk} %}}" hx-swap="outerHTML" hx-trigger="keyup delay:2s" class="form-control score" style="background-color:{ bgcode }" title="{ grade_score }" name="score" id="input-{{ forloop.counter0 }}" placeholder="{ grade.score }" required>'
else:
bgcode = get_color(grade.score)
input_string=f'<input type="text" hx-post="{{% url "gradebook:grade-change" { grade.pk} %}}" hx-swap="outerHTML" hx-trigger="keyup delay:2s" class="form-control score" style="background-color:{ bgcode }; border:solid rgb(255, 0, 0,.5);" title="xxx" name="score" id="input-{{ forloop.counter0 }}" placeholder="{ new_score }" required>'
return HttpResponse(input_string)
模板
<div class="container ps-4">
<div class="row">
<div class="col-2">
<h5>{{ assessment.assessment_name }}</h5>
</div>
<div class="col-4">Class: {{ classblock }}, Assessment Date: {{ assessment.date_created|date:'Y-m-d' }}
</div>
<div class="col-2" id="edit">
<p>Click an item to edit.</p>
</div>
<div class="col-4">
<a href="{% url 'gradebook:addassessment' course_pk %}"><button type="submit" class="btn btn-secondary">Return to Assessment List</button></a>
</div>
<hr/>
</div>
{% if objective_list %}
<div class="row" id="create">
<div class="col-md-3">
<div>No grades entered yet.</div>
</div>
<div class="col-md-3">
<a href="{% url 'gradebook:addgrades' assess_pk class_pk %}"><button class="btn btn-primary">Add Grades</button></a>
</div>
</div>
<div class="table-responsive" id = "grade-table">
<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">
<div id="hxtarget">test</div>
{% 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-swap="outerHTML" 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>
</div>
{% else %}
<p>No objectives. Please add an objective to this assignment.</p>
{% endif %}
</div>
urls
path('assessmentdetail/<uuid:assess_pk>/<uuid:class_pk>/',
views.assessmentdetail, name='assessdetail'),
path('grade-change/<uuid:pk>/', views.GradeChange.as_view(), name='grade-change'),
我在控制台中得到的错误是:
Request URL: http://127.0.0.1:8000/gradebook/assessmentdetail/3f4c7422-89d0-442c-a310-46aff8123949/be8ef548-25a7-4793-99d1-ff3cc4166921/%7B%%20url
Request Method: POST
Status Code: 404 Not Found```
在我的浏览器中此页面的 url 是 http://127.0.0.1:8000/gradebook/assessmentdetail/3f4c7422-89d0-442c-a310-46aff8123949/be8ef548-25a7-4793-99d1-ff3cc4166921/
。我不知道 %7B%%20url
来自哪里。我对 htmx 的理解不够深入,不知道为什么要这样调用 url.
问题是响应不是 HTML 而是未呈现的 Django 模板代码,这显然不能在前端运行。您必须 return 纯 HTML(或使用前端模板系统,但这不是您问题的主题。)
有问题的变量是 input_string
:
input_string=f'<input type="text" hx-post="{{% url "gradebook:grade-change" { grade.pk} %}}" hx-swap="outerHTML" hx-trigger="keyup delay:2s" class="form-control score" style="background-color:{ bgcode }" title="{ grade_score }" name="score" id="input-{{ forloop.counter0 }}" placeholder="{ grade.score }" required>'
HTMX 认为这是有效的 HTML,因此 hx-post
属性将只是:hx-post="{{% url "
。那是因为 Django 模板的 url
没有在后端进行评估。所以你的日志中有奇怪的 %7B%%20url
字符串的来源,%7B
是 {
括号的 URL-encoded 版本。
修复非常简单,只需要在后端使用reverse()
方法:
from django.urls import reverse
input_string=f'<input type="text" hx-post="{reverse("gradebook:grade-change", args=[grade.pk])}" hx-swap="outerHTML" hx-trigger="keyup delay:2s" class="form-control score" style="background-color:{ bgcode }" title="{ grade_score }" name="score" placeholder="{ grade.score }" required>'
注意:我已经删除了 id="input-{{ forloop.counter0 }}"
,因为它似乎没有必要并且需要从前端传递循环计数器。这当然是可能的,例如hx-vals
如果你需要的话。
我有 table 个单元格,每个单元格都有不同的对象实例,并使用 htmx 更新对象。我创建了一个 CBV,它从 htmx 中获取 request.post 并将修改后的对象保存到数据库中。 htmx 执行 hx-swap 并将新的 <input>
标记加载到我的表单中,以及一些基于保存的对象的背景颜色样式。我可以点击许多单元格并以这种方式更新多个对象。这按预期工作,我没有看到任何错误。
但是,我第二次尝试更新同一个 cell/object 时,出现了 403 错误。此错误不会显示在浏览器中。细胞似乎没有反应。我可以继续更新其他单元格。该错误出现在我的浏览器控制台中。
观看次数
asessmentdetail - 加载模板并检索所有对象以填充 table
的视图def assessmentdetail(request, assess_pk, class_pk):
"""List the current grades for the assessment"""
user = request.user
assessment = Assessment.objects.get(id=assess_pk)
classblock = Classroom.objects.get(id=class_pk)
course_pk = classblock.course.pk
objective_list = Objective.objects.all().filter(
assessment=assess_pk).order_by('objective_name')
student_list = Student.objects.all().filter(
classroom=class_pk).order_by('nickname')
gra = Grade.objects.filter(
assessment=assess_pk).filter(cblock=classblock.id)
context = {'objective_list': objective_list}
new_or_update = gra.exists()
grade_report = [str(new_or_update)]
grade_list = []
grade_id_array = []
grade_report.append(len(objective_list))
# print(len(objective_list))
comment_list = []
n = 0
if gra:
for student in student_list:
# grade_report.append(student.nickname)
comms = AssessComment.objects.filter(
student=student, assessment=assessment).first()
if comms:
comment_list.append(comms)
else:
new_c = AssessComment(user=user,
student=student, assessment=assessment, comment="---")
new_c.save()
comment_list.append(new_c)
for obj in objective_list:
if gra.filter(objective=obj, student=student.id).last():
grade_report.append(q.score)
grade_id_array.append(q.id)
grade_list.append(q)
n = n + 1
context['grade_report'] = grade_report
context['grade_list'] = grade_list
context['grade_id_array'] = grade_id_array
context['student_list'] = student_list
context['assessment'] = assessment
context['class_pk'] = class_pk
context['assess_pk'] = assess_pk
context['course_pk'] = course_pk
context['comment_list'] = comment_list
context['classblock'] = classblock
)
return render(request, "gradebook/assessment_detail.html", context)
GradeChange - 通过htmx修改对象的视图
class GradeChange(SingleObjectMixin, View):
""" view to handle htmx grade change"""
model = Grade
def post(self, request, *args, **kwargs):
grade = self.get_object()
ns = request.POST.get('score')
new_score = ns.upper()
def get_color(grade): # map background color to the score
if grade == "EXT":
convert_code = "rgba(153,102,255,0.4)"
elif grade == "APP+" or grade == "PRF+":
convert_code = "rgba(75, 192, 192, 0.7)"
elif grade == "APP" or grade == "PRF":
convert_code = "rgba(75, 192, 192, 0.3)"
elif grade == "DEV":
convert_code = "rgba(255, 205, 86, 0.4)"
elif grade == "EMG" or grade == "BEG":
convert_code = "rgba(225, 99, 132, 0.4)"
else:
convert_code = "rgba(0, 0, 0, 0.1)"
return (convert_code)
score_list = ["EXT", "APP+", "PRF+", "APP", "PRF", "DEV", "BEG", "EMG", "I", "---"]
if new_score in score_list:
grade.score = new_score
grade.save()
grade_score=str(grade.score)
bgcode = get_color(grade.score)
input_string=f'<input type="text" hx-post="{{% url "gradebook:grade-change" { grade.pk} %}}" hx-swap="outerHTML" hx-trigger="keyup delay:2s" class="form-control score" style="background-color:{ bgcode }" title="{ grade_score }" name="score" id="input-{{ forloop.counter0 }}" placeholder="{ grade.score }" required>'
else:
bgcode = get_color(grade.score)
input_string=f'<input type="text" hx-post="{{% url "gradebook:grade-change" { grade.pk} %}}" hx-swap="outerHTML" hx-trigger="keyup delay:2s" class="form-control score" style="background-color:{ bgcode }; border:solid rgb(255, 0, 0,.5);" title="xxx" name="score" id="input-{{ forloop.counter0 }}" placeholder="{ new_score }" required>'
return HttpResponse(input_string)
模板
<div class="container ps-4">
<div class="row">
<div class="col-2">
<h5>{{ assessment.assessment_name }}</h5>
</div>
<div class="col-4">Class: {{ classblock }}, Assessment Date: {{ assessment.date_created|date:'Y-m-d' }}
</div>
<div class="col-2" id="edit">
<p>Click an item to edit.</p>
</div>
<div class="col-4">
<a href="{% url 'gradebook:addassessment' course_pk %}"><button type="submit" class="btn btn-secondary">Return to Assessment List</button></a>
</div>
<hr/>
</div>
{% if objective_list %}
<div class="row" id="create">
<div class="col-md-3">
<div>No grades entered yet.</div>
</div>
<div class="col-md-3">
<a href="{% url 'gradebook:addgrades' assess_pk class_pk %}"><button class="btn btn-primary">Add Grades</button></a>
</div>
</div>
<div class="table-responsive" id = "grade-table">
<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">
<div id="hxtarget">test</div>
{% 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-swap="outerHTML" 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>
</div>
{% else %}
<p>No objectives. Please add an objective to this assignment.</p>
{% endif %}
</div>
urls
path('assessmentdetail/<uuid:assess_pk>/<uuid:class_pk>/',
views.assessmentdetail, name='assessdetail'),
path('grade-change/<uuid:pk>/', views.GradeChange.as_view(), name='grade-change'),
我在控制台中得到的错误是:
Request URL: http://127.0.0.1:8000/gradebook/assessmentdetail/3f4c7422-89d0-442c-a310-46aff8123949/be8ef548-25a7-4793-99d1-ff3cc4166921/%7B%%20url
Request Method: POST
Status Code: 404 Not Found```
在我的浏览器中此页面的 url 是 http://127.0.0.1:8000/gradebook/assessmentdetail/3f4c7422-89d0-442c-a310-46aff8123949/be8ef548-25a7-4793-99d1-ff3cc4166921/
。我不知道 %7B%%20url
来自哪里。我对 htmx 的理解不够深入,不知道为什么要这样调用 url.
问题是响应不是 HTML 而是未呈现的 Django 模板代码,这显然不能在前端运行。您必须 return 纯 HTML(或使用前端模板系统,但这不是您问题的主题。)
有问题的变量是 input_string
:
input_string=f'<input type="text" hx-post="{{% url "gradebook:grade-change" { grade.pk} %}}" hx-swap="outerHTML" hx-trigger="keyup delay:2s" class="form-control score" style="background-color:{ bgcode }" title="{ grade_score }" name="score" id="input-{{ forloop.counter0 }}" placeholder="{ grade.score }" required>'
HTMX 认为这是有效的 HTML,因此 hx-post
属性将只是:hx-post="{{% url "
。那是因为 Django 模板的 url
没有在后端进行评估。所以你的日志中有奇怪的 %7B%%20url
字符串的来源,%7B
是 {
括号的 URL-encoded 版本。
修复非常简单,只需要在后端使用reverse()
方法:
from django.urls import reverse
input_string=f'<input type="text" hx-post="{reverse("gradebook:grade-change", args=[grade.pk])}" hx-swap="outerHTML" hx-trigger="keyup delay:2s" class="form-control score" style="background-color:{ bgcode }" title="{ grade_score }" name="score" placeholder="{ grade.score }" required>'
注意:我已经删除了 id="input-{{ forloop.counter0 }}"
,因为它似乎没有必要并且需要从前端传递循环计数器。这当然是可能的,例如hx-vals
如果你需要的话。