我第一次在 django 中创建一个 Web 应用程序,其中一个功能是添加和编辑职位空缺。使用表单和 POST 方法添加一些信息(职位空缺的标题、描述等),并使用 Fetch api(职责、优惠、优势)在 DOM 和数据库中动态添加和删除其他字段。
加载编辑职位空缺视图时,会从数据库中预加载职责、优惠和优势以及表单数据。
当尝试从编辑职位空缺视图中删除预加载的职责、优惠和优势时,就会出现问题。当我发送 DELETE 请求时,某些内容会在编辑视图中触发 GET,导致所有内容重新加载。这并不理想,因为所有以前的用户输入都消失了。
这只发生在编辑视图中,新的空缺视图按预期工作,添加和删除动态字段而无需刷新页面。
如何防止来自已删除任务的 DELETE 请求在编辑时触发 GET?为什么会发生这种情况?
视频演示:
预期行为:链接(在添加新职位空缺时有效。添加和删除项目无需刷新。日志按预期显示 PUT 和 DELETE 请求。):
bug: link(页面当前在编辑视图中的行为方式。删除项目后页面刷新。DELETE 请求后紧跟着不需要的 GET 请求):
编辑views.py:
@login_required(login_url="/login")
def edit(request,id):
if request.method =="POST":
vacancy = Vacancy.objects.get(pk=id)
newtitle = request.POST['title']
newcity = request.POST['city']
newcountry = request.POST['country']
newimage = request.FILES.get('image')
newdescription = request.POST['description']
vacancy.title = newtitle
vacancy.city = newcity
vacancy.country = newcountry
vacancy.description = newdescription
if newimage:
vacancy.image = newimage
vacancy.save()
if vacancy.image:
return HttpResponseRedirect(reverse("dashboard"))
else:
return render(request,'app/cms-edit.html',{
'vacancy': vacancy,
'imagemessage': 'Please add an image'
})
if request.method=="GET":
vacancy = Vacancy.objects.get(pk=id)
title = vacancy.title
city = vacancy.city
country = vacancy.country
description = vacancy.description
image = vacancy.image
duties = Duty.objects.filter(vacancy=vacancy)
offers = Offer.objects.filter(vacancy=vacancy)
advantages = Advantage.objects.filter(vacancy=vacancy)
return render(request,'app/cms-edit.html',{
'vacancy': vacancy,
'duties':duties,
'offers':offers,
'advantages':advantages,
})
删除职责视图.py:
@csrf_exempt
def removeduty(request,id):
duty = Duty.objects.get(pk=id)
if request.method == "DELETE":
duty.delete()
return JsonResponse({"message":"duty deleted"})
else:
return JsonResponse({"message":"duty not deleted"})
删除职责 javascript
//remove duty
function removeduty(dutyid) {
let csrftoken = getCookie('csrftoken');
let parent = document.getElementById(`dutyitem-${dutyid}`);
parent.remove()
fetch(`removeduty/${dutyid}`, {
method: 'DELETE',
headers: { "X-CSRFToken": csrftoken }
})
.then(response => response.json())
.then(result => {
console.log(result.message);
})
.catch(error => {
console.error('Error deleting duty:', error);
});
}
编辑职位空缺html的相关部分
<div id="dutysection" class="dutyitems">
{% if duties %}
{% for duty in duties%}
<div class="cms-added-item" id="dutyitem-{{duty.id}}">
<p class="cms-added-list-item">{{duty.text}}</p>
<div class="action-wrap-app">
<a href="" class="trash-icon w-inline-block" onclick ="removeduty({{duty.id}})">
<div class="delete w-embed"><svg width="auto" height="32" viewbox="0 0 36 39" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.0841 29.526L18.0363 23.4738L24.0504 29.526L26.8744 26.6641L20.9102 20.5999L26.8744 14.5358L24.0504 11.6619L18.0363 17.726L12.0841 11.6619L9.22219 14.5358L15.2124 20.5999L9.22219 26.6641L12.0841 29.526ZM6.87654 38.8597C5.85041 38.8597 4.94768 38.4732 4.16834 37.7002C3.38904 36.9272 2.99939 36.0253 2.99939 34.9945V6.51948H0.746094V2.65428H11.157V0.798828H24.8657V2.65428H35.2765V6.51948H33.0233V34.9945C33.0233 36.0253 32.6356 36.9272 31.8603 37.7002C31.085 38.4732 30.1802 38.8597 29.1461 38.8597H6.87654Z" fill="currentColor"></path>
</svg></div>
</a>
</div>
</div>
{% endfor%}
{% else %}
{% endif %}
</div>
相关网址模式
path("edit/<int:id>",views.edit,name="edit"),
path("edit/addduty/<int:id>",views.addduty,name="addduty"),
path("edit/removeduty/<int:id>",views.removeduty,name="removeduty"),
path("edit/addoffer/<int:id>",views.addoffer,name="addoffer"),
path("edit/removeoffer/<int:id>",views.removeoffer,name="removeoffer"),
path("edit/addadvantage/<int:id>",views.addadvantage,name="addadvantage"),
path("edit/removeadvantage/<int:id>",views.removeadvantage,name="removeadvantage"),
以下是removeduty函数运行时发生的情况的日志。您可以看到它在编辑时触发 GET,导致其刷新。但为什么?
[2023年8月7日09:03:47]“删除/edit/removeduty/202 HTTP/1.1”200 27
[2023年8月7日09:03:47]“获取/编辑/250 HTTP/1.1”200 8895
编辑:我尝试为编辑视图添加另一个 if 条件,以返回 if request.method == "DELETE",看看这是否会阻止它发生,但这没有任何作用。
我怀疑这个问题可能与我设置网址模式的方式有关。我为编辑视图创建了单独的模式,因为常规模式不起作用。
这里是用于从添加新空缺视图创建项目的 url 模式(按预期工作)
path("addduty/<int:id>",views.addduty,name="addduty"),
path("removeduty/<int:id>",views.removeduty,name="removeduty"),
path("addoffer/<int:id>",views.addoffer,name="addoffer"),
path("removeoffer/<int:id>",views.removeoffer,name="removeoffer"),
path("addadvantage/<int:id>",views.addadvantage,name="addadvantage"),
path("removeadvantage/<int:id>",views.removeadvantage,name="removeadvantage"),
这是否会导致某种冲突,从而触发 GET 请求并导致刷新?
我通过添加“javascript:void(0);”解决了该问题在删除按钮的 href 标签中而不是“#”或将其留空。
<a href="javascript:void(0);" class="trash-icon w-inline-block" onclick ="removeduty({{duty.id}})">
与将
duty.id
作为参数直接提供给内联函数相比,使用外部注册的事件处理程序会更干净,该事件处理程序检查(单击)event
来识别元素,并从中推断出分配的 duty.id
到 event.target 或合适的父级。
如果您要将所有 SVG 图像的所有路径存储在其他地方(见下文),则内联 SVG 图像可以替换为
use
元素 - 这将使 HTML 更小,因为完整路径仅写入一次而不是几十次次。
事件处理程序正在侦听 SVG 元素上的点击,因此超链接现在除了提供在事件处理程序中使用的
data-dutyid
值之外实际上不执行任何操作。在代码片段中,duty.id
值仅作为“示例”被硬编码数值替换 - 这些在实际代码中将是{{duty.id}}
。
const getCookie=(name)=>`Some code to find a cookie named:"${name}"`;
const getresponse=(r)=>r.json();
const callback=(r)=>console.log(r.message);
const errorhandler=(e)=>console.warn('Error deleting duty: %o',e);
document.addEventListener('click',e=>{
if( e.target instanceof SVGUseElement || e.target instanceof SVGSVGElement ){
e.preventDefault();
let dutyid=e.target.closest('a').dataset.dutyid;
let config={
method:'delete',
headers:{ 'X-CSRFToken':getCookie('csrftoken') }
};
e.target.closest('div.cms-added-item').remove();
fetch( `removeduty/${dutyid}`, config )
.then( getresponse )
.then( callback )
.catch( errorhandler )
}
});
<div id="dutysection" class="dutyitems">
<!-- example hardcoded items - loop removed -->
<div class="cms-added-item">
<p class=" cms-added-list-item">BANANA</p>
<div class="action-wrap-app">
<a href="#" class="trash-icon w-inline-block" data-dutyid="303">
<div class="delete w-embed">
<svg viewBox="0 0 36 39" height="32" fill="none" preserveAspectRatio="xMinYMin">
<use href="#trashcan" />
</svg>
</div>
</a>
</div>
</div>
<div class="cms-added-item">
<p class=" cms-added-list-item">WOMBLE</p>
<div class="action-wrap-app">
<a href="#" class="trash-icon w-inline-block" data-dutyid="304">
<div class="delete w-embed">
<svg viewBox="0 0 36 39" height="32" fill="none" preserveAspectRatio="xMinYMin">
<use href="#trashcan" />
</svg>
</div>
</a>
</div>
</div>
<div class="cms-added-item">
<p class=" cms-added-list-item">{{duty.text}}</p>
<div class="action-wrap-app">
<a href="#" class="trash-icon w-inline-block" data-dutyid="{{duty.id}}">
<div class="delete w-embed">
<svg viewBox="0 0 36 39" height="32" fill="none" preserveAspectRatio="xMinYMin">
<use href="#trashcan" />
</svg>
</div>
</a>
</div>
</div>
</div>
<!--
If you use lots of SVG images on the page, rather than add the full path inline with the HTML
every time you add the image it would be better to maintain the `path` elements within a `defs`
element as below and call it using `use`
-->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" aria-hidden="true" style="position:absolute; width:0; height:0; overflow:hidden; display:none">
<defs>
<symbol id="trashcan">
<path d="M12.0841 29.526L18.0363 23.4738L24.0504 29.526L26.8744 26.6641L20.9102 20.5999L26.8744 14.5358L24.0504 11.6619L18.0363 17.726L12.0841 11.6619L9.22219 14.5358L15.2124 20.5999L9.22219 26.6641L12.0841 29.526ZM6.87654 38.8597C5.85041 38.8597 4.94768 38.4732 4.16834 37.7002C3.38904 36.9272 2.99939 36.0253 2.99939 34.9945V6.51948H0.746094V2.65428H11.157V0.798828H24.8657V2.65428H35.2765V6.51948H33.0233V34.9945C33.0233 36.0253 32.6356 36.9272 31.8603 37.7002C31.085 38.4732 30.1802 38.8597 29.1461 38.8597H6.87654Z" fill="currentColor"></path>
</symbol>
</defs>
</svg>