我有一个带有3个输入和步骤按钮的html表单。
每当用户按下任意按钮,然后转到下一个html步骤。
我希望在用户按下任意按钮时逐步在views.py
中处理输入,并且在最终提交中不全部合并。
我在views.py
中尝试了此代码以在django后端中进行输入,但在views.py
中却什么也没有得到(如果我将按钮类型从button
更改为submit
那么我会得到结果螺母刷新页面,而我无法转到步骤2)
if request.method == 'POST' and 'first_step' in request.POST:
print '1'
firstname= request.POST.get('firstname')
if request.method == 'POST' and 'second_step' in request.POST:
print '2'
lastname= request.POST.get('lastname')
if request.method == 'POST' and 'final_step' in request.POST:
print '3'
email= request.POST.get('email')
这是我的html代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Form wizard with circular tabs</title>
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<div class="row">
<section>
<div class="wizard">
<div class="wizard-inner">
<div class="connecting-line"></div>
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active">
<a href="#step1" data-toggle="tab" aria-controls="step1" role="tab" title="Step 1">
<span class="round-tab">
<i class="glyphicon glyphicon-folder-open"></i>
</span>
</a>
</li>
<li role="presentation" class="disabled">
<a href="#step2" data-toggle="tab" aria-controls="step2" role="tab" title="Step 2">
<span class="round-tab">
<i class="glyphicon glyphicon-pencil"></i>
</span>
</a>
</li>
<li role="presentation" class="disabled">
<a href="#step3" data-toggle="tab" aria-controls="step3" role="tab" title="Step 3">
<span class="round-tab">
<i class="glyphicon glyphicon-picture"></i>
</span>
</a>
</li>
<li role="presentation" class="disabled">
<a href="#complete" data-toggle="tab" aria-controls="complete" role="tab" title="Complete">
<span class="round-tab">
<i class="glyphicon glyphicon-ok"></i>
</span>
</a>
</li>
</ul>
</div>
<form role="form">
<div class="tab-content">
<div class="tab-pane active" role="tabpanel" id="step1">
<div class="step1">
<div class="row">
<div class="col-md-6">
<label for="exampleInputEmail1">First Name</label>
<input type="email" name="firstname" class="form-control" id="exampleInputEmail1" placeholder="First Name">
</div>
</div>
</div>
<ul class="list-inline pull-right">
<li><button type="button" name="first_step" class="btn btn-primary next-step">Save and continue</button></li>
</ul>
</div>
<div class="tab-pane" role="tabpanel" id="step2">
<div class="step2">
<div class="step-22">
<label for="exampleInputEmail1">Last Name</label>
<input type="email" name="lastname" class="form-control" id="exampleInputEmail1" placeholder="Last Name">
</div>
</div>
<ul class="list-inline pull-right">
<li><button type="button" class="btn btn-default prev-step">Previous</button></li>
<li><button type="button" name="second_step" class="btn btn-primary next-step">Save and continue</button></li>
</ul>
</div>
<div class="tab-pane" role="tabpanel" id="step3">
<div class="step33">
<label for="exampleInputEmail1">email</label>
<input type="email" name="email" class="form-control" id="exampleInputEmail1" placeholder="Last Name">
</div>
<ul class="list-inline pull-right">
<li><button type="button" class="btn btn-default prev-step">Previous</button></li>
<li><button type="button" name="final_step" class="btn btn-primary btn-info-full next-step">Save and continue</button></li>
</ul>
</div>
<div class="tab-pane" role="tabpanel" id="complete">
<div class="step44">
<h5>Completed</h5>
</div>
</div>
<div class="clearfix"></div>
</div>
</form>
</div>
</section>
</div>
</div>
<script type="text/javascript">
$(document).ready(function () {
//Initialize tooltips
$('.nav-tabs > li a[title]').tooltip();
//Wizard
$('a[data-toggle="tab"]').on('show.bs.tab', function (e) {
var $target = $(e.target);
if ($target.parent().hasClass('disabled')) {
return false;
}
});
$(".next-step").click(function (e) {
var $active = $('.wizard .nav-tabs li.active');
$active.next().removeClass('disabled');
nextTab($active);
});
$(".prev-step").click(function (e) {
var $active = $('.wizard .nav-tabs li.active');
prevTab($active);
});
});
function nextTab(elem) {
$(elem).next().find('a[data-toggle="tab"]').click();
}
function prevTab(elem) {
$(elem).prev().find('a[data-toggle="tab"]').click();
}
//according menu
$(document).ready(function()
{
//Add Inactive Class To All Accordion Headers
$('.accordion-header').toggleClass('inactive-header');
//Set The Accordion Content Width
var contentwidth = $('.accordion-header').width();
$('.accordion-content').css({});
//Open The First Accordion Section When Page Loads
$('.accordion-header').first().toggleClass('active-header').toggleClass('inactive-header');
$('.accordion-content').first().slideDown().toggleClass('open-content');
// The Accordion Effect
$('.accordion-header').click(function () {
if($(this).is('.inactive-header')) {
$('.active-header').toggleClass('active-header').toggleClass('inactive-header').next().slideToggle().toggleClass('open-content');
$(this).toggleClass('active-header').toggleClass('inactive-header');
$(this).next().slideToggle().toggleClass('open-content');
}
else {
$(this).toggleClass('active-header').toggleClass('inactive-header');
$(this).next().slideToggle().toggleClass('open-content');
}
});
return false;
});
</script>
</body>
</html>
答案 0 :(得分:5)
几个小时前,我写了这个问题的答案,然后我删除了它,因为我不得不意识到我只是部分地给出了解决这个问题的方法,因为这个问题比最初看起来要复杂得多。
OP写道:如果您使用 type =“ submit” 按钮输入,则将提交输入,但同时页面将刷新并使用此表单 < / strong>这不是目的。而且,如果您使用 type =“ button” 输入,那么输入数据将不会到达服务器(仅当您将提交的数据收集到javascript对象中,然后引发AJAX调用并将其发送时)到具有该AJAX请求的服务器)。
基本上,这也不是Django问题,而更像是javascript / AJAX调用问题。并且还调用了一些安全问题来解决。由于使用提交,您还必须以某种方式将CSRF令牌发送到服务器。因此,可以解决此问题,这对任何人来说都将花费一些时间。
有关此主题的好资料在这里: https://simpleisbetterthancomplex.com/tutorial/2016/08/29/how-to-work-with-ajax-request-with-django.html(但是,本文的内容在某种程度上是没有用的,在这种情况下无效)
这就是它的工作方式
很久以前,我还没有使用Django和Python(如今更喜欢使用PHP和Joomla),但我只是在Python 3.7上安装了Django 2.1.3,以确保它是有效(以下内容也应在较旧的版本中工作,如果需要,只需进行很少的修改)
我创建了一个名为“ myuserform”的应用,并首先在 models.py
中创建了一个模型models.py
from django.db import models
from django.utils import timezone
class NewUser(models.Model):
first_name = models.CharField(max_length=150)
last_name = models.CharField(max_length=150)
email = models.EmailField(max_length=254)
creation_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.first_name, self.last_name
然后我在Forms.py中创建了一个Form(重要的是:如果您在Django中创建Forms,请先创建一个Model,然后在Django中创建ModelForm,这是您正确执行这些工作的方式)
forms.py
from django import forms
from django.forms import ModelForm
from myuserform.models import NewUser
# Create the form class.
class NewUserForm(ModelForm):
class Meta:
model = NewUser
fields = ['first_name', 'last_name', 'email']
既然,上面的OP已经给出了 HTML表单,那么我就在我的应用程序“ myuserform”的 templates 文件夹中使用它们创建了两个模板。 base.html 和 regform.html (我现在不关心创建漂亮的模板)
我还通过向按钮添加 onclick 属性来触发不同的自定义javascript函数,从而清除了一些输入字段以使其与javascript代码一起正常工作(使用jQuery元素可以大大简化此操作)当然选择)。 最后一个按钮将通过AJAX提交表单。(您不必将输入数据单独发送或收集到Django,对我来说这是多余的,因为您要做什么?想要处理名字输入数据,例如“ Joe” ?什么都没有。您也可以使用javascript逐步验证输入数据-我也添加了这些功能,但是可以进一步扩展这些验证前的功能。现在,它仅检查该字段是否为空,并且电子邮件字段是否为有效的电子邮件格式输入,如果不是,则不允许您进行进一步操作。
在 templates / base.html
中<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/js-cookie@2/src/js.cookie.min.js"></script>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<title>{% block title %}My amazing site homepage{% endblock %}</title>
</head>
<body>
<div id="content">
{% block content %}{% endblock %}
</div>
</body>
</html>
在 templates / regform.html
中{% extends "base.html" %}
{% block title %}My amazing Registration Form{% endblock %}
{% block content %}
<h1>{{title}}</h1><br>
<div class="container">
<div class="row">
<div class="col-md-6">
<section>
<div class="wizard">
<div class="wizard-inner">
<div class="connecting-line"></div>
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active">
<a href="#step1" data-toggle="tab" aria-controls="step1" role="tab" title="Step 1">
<span class="round-tab">
<i class="glyphicon glyphicon-folder-open"></i>
</span>
</a>
</li>
<li role="presentation" class="disabled">
<a href="#step2" data-toggle="tab" aria-controls="step2" role="tab" title="Step 2">
<span class="round-tab">
<i class="glyphicon glyphicon-pencil"></i>
</span>
</a>
</li>
<li role="presentation" class="disabled">
<a href="#step3" data-toggle="tab" aria-controls="step3" role="tab" title="Step 3">
<span class="round-tab">
<i class="glyphicon glyphicon-picture"></i>
</span>
</a>
</li>
<li role="presentation" class="disabled">
<a href="#complete" data-toggle="tab" aria-controls="complete" role="tab" title="Complete">
<span class="round-tab">
<i class="glyphicon glyphicon-ok"></i>
</span>
</a>
</li>
</ul>
</div>
<form role="form" id="login-form" action="#" method="post">
{% csrf_token %}
<div class="tab-content">
<div class="tab-pane active" role="tabpanel" id="step1">
<div class="step1">
<div class="row">
<div class="col-md-6">
<label for="exampleInputEmail1">First Name</label>
<input type="text" name="first_name" class="form-control" id="exampleInputEmail1" placeholder="First Name">
</div>
</div>
</div>
<ul class="list-inline pull-right">
<li><button type="button" name="first_step" class="btn btn-primary" onclick="getFirstNameMove()">Save and continue</button></li>
</ul>
</div>
<div class="tab-pane" role="tabpanel" id="step2">
<div class="step2">
<div class="step-22">
<label for="exampleInputEmail1">Last Name</label>
<input type="text" name="last_name" class="form-control" id="exampleInputEmail2" placeholder="Last Name">
</div>
</div>
<ul class="list-inline pull-right">
<li><button type="button" class="btn btn-default prev-step">Previous</button></li>
<li><button type="button" name="second_step" class="btn btn-primary" onclick="getLastNameMove()">Save and continue</button></li>
</ul>
</div>
<div class="tab-pane" role="tabpanel" id="step3">
<div class="step3">
<div class="step-33">
<label for="exampleInputEmail1">email</label>
<input type="email" name="email" class="form-control" id="exampleInputEmail3" placeholder="email address">
</div>
<ul class="list-inline pull-right">
<li><button type="button" class="btn btn-default prev-step">Previous</button></li>
<li><button type="button" name="final_step" id="final_step" class="btn btn-primary btn-info-full" onclick="getEmailMove()">Save and continue</button></li>
</ul>
</div>
</div>
<div class="tab-pane" role="tabpanel" id="complete">
<div class="step44">
<h5>Completed</h5>
</div>
</div>
<div class="clearfix"></div>
</div>
</form>
</div>
</section>
</div>
</div>
</div>
<script type="text/javascript">
$ = jQuery.noConflict();
$(document).ready(function () {
//Initialize tooltips
$('.nav-tabs > li a[title]').tooltip();
//Wizard
$('a[data-toggle="tab"]').on('show.bs.tab', function (e) {
var $target = $(e.target);
if ($target.parent().hasClass('disabled')) {
return false;
}
});
$(".next-step").click(function (e) {
var $active = $('.wizard .nav-tabs li.active');
$active.next().removeClass('disabled');
nextTab($active);
});
$(".prev-step").click(function (e) {
var $active = $('.wizard .nav-tabs li.active');
prevTab($active);
});
});
function getFirstNameMove() {
if (checkFirstName()) {
moveNextTab();
}
}
function getLastNameMove() {
if (checkLastName()) {
moveNextTab();
}
}
function getEmailMove() {
if (checkEmail()) {
moveNextTab();
sendNuData();
}
}
function checkFirstName() {
form = document.getElementById('login-form');
if (form.first_name.value == '') {
alert('Cannot leave First name field blank.');
form.first_name.focus();
return false;
}
return true;
}
function checkLastName() {
form = document.getElementById('login-form');
if (form.last_name.value == '') {
alert('Cannot leave Last name field blank.');
form.last_name.focus();
return false;
}
return true;
}
function checkEmail() {
form = document.getElementById('login-form');
let newEmail = form.email.value;
if (newEmail == '') {
alert('Cannot leave email field blank.');
form.email.focus();
return false;
}
if (emailValidate(newEmail)) {
return true;
} else {
alert('Please provide a valid email address.');
form.email.focus();
return false;
}
}
function emailValidate(sEmail) {
let filter = /^([\w-.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(]?)$/;
return filter.test(sEmail);
}
function moveNextTab() {
var $active = $('.wizard .nav-tabs li.active');
$active.next().removeClass('disabled');
nextTab($active);
}
function nextTab(elem) {
$(elem).next().find('a[data-toggle="tab"]').click();
}
function prevTab(elem) {
$(elem).prev().find('a[data-toggle="tab"]').click();
}
function sendNuData(){
$.ajaxSetup({
beforeSend: function(xhr, settings) {
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
// Only send the token to relative URLs i.e. locally.
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
}
});
$.ajax({
url: '/get_allform_data/',
method: 'post',
data: $('form').serialize(),
contentType: false,
success: function (data) {
alert('Form Submitted');
// console.log(data);
},
error: function(data) {
alert('Form submission failed');
// console.log(data);
}
});
}
//according menu
$(document).ready(function()
{
//Add Inactive Class To All Accordion Headers
$('.accordion-header').toggleClass('inactive-header');
//Set The Accordion Content Width
var contentwidth = $('.accordion-header').width();
$('.accordion-content').css({});
//Open The First Accordion Section When Page Loads
$('.accordion-header').first().toggleClass('active-header').toggleClass('inactive-header');
$('.accordion-content').first().slideDown().toggleClass('open-content');
// The Accordion Effect
$('.accordion-header').click(function () {
if($(this).is('.inactive-header')) {
$('.active-header').toggleClass('active-header').toggleClass('inactive-header').next().slideToggle().toggleClass('open-content');
$(this).toggleClass('active-header').toggleClass('inactive-header');
$(this).next().slideToggle().toggleClass('open-content');
}
else {
$(this).toggleClass('active-header').toggleClass('inactive-header');
$(this).next().slideToggle().toggleClass('open-content');
}
});
return false;
});
</script>
{% endblock %}
然后是最困难的部分之一,它是如何处理/保存从表单提交中发送给Django的AJAX调用数据的问题 strong>(这样就不会通过普通的提交按钮(使用普通的HTTP请求)提交表单,这将是一个众所周知的,相对容易处理的案例和任务)。
有两件事:通过AJAX调用向Django提交并发送html表单输入数据时,您会遇到麻烦:
1。。请求数据将采用 WSGI请求对象,否则,将采用不可变Querydict 格式,该格式无法由只是对它们调用普通的Querydict方法。
2。无法从常规的 request.POST 数据填充新的 Form对象,因为它将为空(如果contentType设置为设置为false,例如AJAX调用中的 contentType:false )。这两点在Django中没有很好地记录。
如果 contentType 为留空或设置为:
contentType: "application/x-www-form-urlencoded",
然后,您可以使用以下命令获取所有提交的输入字段的值:
first_name = request.POST.get('first_name')
last_name = request.POST.get('last_name') # and so on...
但是在这里,我只是使用普通的请求对象 在视图中填充表单。py
因此,我不得不创建一个视图来处理AJAX请求。它是 get_allform_data()视图(它可能有多种显示方式,我只是制作了一个版本)。最后,它很简单,但是对于普通的Django开发人员来说,这绝对不是日常工作,因此最好了解这些。
所以 views.py
from django.template import Template, Context
from django.template.loader import get_template
from django.shortcuts import render
from django.http import HttpResponseRedirect, HttpResponse, HttpRequest
from django.urls import reverse
from .forms import NewUserForm
from .models import NewUser
from django.forms import Select, ModelForm
import datetime
from django.views.decorators.csrf import csrf_protect
from django.http import QueryDict
import json
import copy
def index(request):
return HttpResponse("Hello, world. You're at the myuserform index.")
@csrf_protect
def regform(request):
title = "This is the Registration Form Page"
return render(request, 'regform.html', {'title': title})
@csrf_protect
def get_allform_data(request):
# you can check if request is ajax
# and you could handle other calls differently
# if request.is_ajax() - do this and do that...
# we create an empty Querydict to copy the request into it
# to be able to handle/modify input request data sent by AJAX
datam = QueryDict()
# we should copy the request to work with it if needed
for i in request:
datam = copy.copy(i)
# the AJAX sent request is better in normal dictionary format
post_dict = QueryDict(datam).dict()
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
# however with using AJAX request.POST is empty - so we use the request Querydict
# to populate the Form
form = NewUserForm(post_dict)
# check whether it's valid:
if form.is_valid():
# you can do whatever with cleaned form data
data = form.cleaned_data
# we can now save the form input data to the database
form.save()
# print("form is now saved")
# return HttpResponse("I got the data and saved it")
else:
print(form.errors)
else:
form = NewUserForm() # this not really needed here, only if we handle the whole in 1 view
# return HttpResponse("I cannot handle the request yet, since it was not sent yet")
return HttpResponseRedirect(reverse('regform'))
return render(request, 'regform.html', {'form' : form })
如果 request.POST 不为空,则以简化形式显示同一视图:
@csrf_protect
def get_allform_data(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = NewUserForm(request.POST)
# check whether it's valid:
if form.is_valid():
# you can still do whatever with the cleaned data here
data = form.cleaned_data
# and you just save the form (inputs) to the database
form.save()
else:
print(form.errors)
else:
form = NewUserForm() # this not really needed here, only if we handle the whole in 1 view
# return HttpResponse("I cannot handle the request yet, since it was not sent yet")
return HttpResponseRedirect(reverse('regform'))
return render(request, 'regform.html', {'form' : form })
最后是 urls.py 文件
from django.contrib import admin
from django.urls import include, path
from myuserform import views as myuserform_views
urlpatterns = [
path('', myuserform_views.index),
path('regform/', myuserform_views.regform, name='regform'),
path('admin/', admin.site.urls),
path('get_allform_data/', myuserform_views.get_allform_data)
]
整个事情可以改进和扩展很多,但是首先,它现在可以完成所需的工作。
以及简短摘要:当然,您可以将输入字段数据逐步发送到Django(使用相同的代码,只需稍作修改),但是在这种特定的Form上绝对没有必要。任务的重点是:如何使用Javascript移动表单选项卡,同时如何收集输入数据,以及如何使用AJAX 向Django发送表单数据以处理/保存表单输入数据到Django。 Django数据库。同时,我们也不想刷新页面。
此屏幕截图直观地显示了最终表单:
答案 1 :(得分:0)
一个可能且未经测试的解决方案将使模型的属性可为空/空白。如下所示,保存发布对象的相关属性。因此,您可以为每个if条件一个一个地设置属性,而不会出现任何null / blank错误。另外,如果您不想每次都刷新页面,则可以使用AJAX。
对于每次点击设置variable=buttonId
中的if
和view
条件,例如if variable == 0
,并且每次通过按钮的数字值作为条件的视图参数,例如{ {1}},保存对象的相关属性,然后使用相同的视图重定向到下一个HTML`。
答案 2 :(得分:0)
我想我知道您要去哪里,但是正如其他用户已经说过的那样,这不是Django问题,而更多是设计问题。
基本上,当您尝试通过提交将信息发布到服务器时,总是会碰壁。如果需要此功能,则需要使用AJAX。
此外,您还有两个实际选择:
Here is a place where you can learn how to use jQuery to accomplish suggestion number 2.
答案 3 :(得分:0)
这个问题对我来说还不是很清楚。有人指出,这可能是设计问题。
通过“按步处理我的views.py中的输入”,对我来说,是否希望将数据发布到每个步骤并由后端处理? (是否使用ajax)
如果希望每个步骤都具有一个不带ajax的响应后循环,则先前的步骤需要成为返回的上下文/模板的一部分,并且可以在后端“处理”该步骤。
在django中,有一个非常强大的工具可以解决这种情况,FormWizard可以为您的特定解决方案提供一些启发吗?