jes*_*jes 9 html javascript python django django-views
我有一个带有3个输入和步骤按钮的html表单.
每当用户按下任何按钮,然后转到下一个html步骤.
我希望views.py在用户按任何按钮的任何时候逐步处理输入,而不是在最终提交中一起处理输入.
我尝试使用此代码在views.pydjango后端获取输入,但我没有得到任何东西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')
Run Code Online (Sandbox Code Playgroud)
这是我的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>
Run Code Online (Sandbox Code Playgroud)
几个小时前我写了这个问题的答案,然后我删除了,因为我必须意识到我只是部分解决了这个问题,因为这个问题比它的第一个看起来要复杂一点.
正如OP写道:如果你使用type ="submit"按钮输入,输入将被提交,但同时页面将刷新,并且使用此表单不是目的.如果您使用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中创建了一个Model
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
Run Code Online (Sandbox Code Playgroud)
然后我在forms.py中创建了一个Form(重要的是:首先创建一个Model,然后在Django中创建一个ModelForm,如果你在Django中创建Forms - 那就是你应该如何正确地完成这些工作)
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']
Run Code Online (Sandbox Code Playgroud)
由于HTML表单已经由上面的OP给出,所以我只是在我的应用程序'myuserform' 的templates文件夹中创建了两个模板.一个base.html和一个regform.html(我现在不关心创建漂亮的模板)
我还清除了一些输入字段,使它们与javascript代码一起使用,并在按钮上添加onclick属性,点亮不同的自定义javascript函数(当然,这可以通过jQuery元素选择进行非常简化).在最后一个按钮将通过AJAX提交表单.(你不必分别向Django发送或收集输入数据,根据我的说法,它是多余的 - 因为你想用firstname输入数据做什么,例如"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>
Run Code Online (Sandbox Code Playgroud)
在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 %}
Run Code Online (Sandbox Code Playgroud)
然后是最困难的部分之一,这是如何处理/或保存AJAX调用从表单提交发送到Django的数据的问题(因此表单不是通过正常的提交按钮(具有正常的HTTP请求)提交的,这将是一个非常着名,相对容易处理的案例和任务).
当您通过AJAX调用向Django提交和发送html表单输入数据时,您会遇到两件事:
1.请求数据将在WSGI Request对象中,否则,是不可变的Querydict格式,只能通过调用它们上的常规Querydict方法来处理.
2.新的Form对象无法从通常的request.POST数据填充,因为它将为空(如果在AJAX调用中contentType设置为false,如contentType:false).这两点在Django中没有很好的记录.
如果contentType中被留空或设置为:
contentType: "application/x-www-form-urlencoded",
Run Code Online (Sandbox Code Playgroud)
然后,您可以使用以下命令获取所有提交的输入字段的值:
first_name = request.POST.get('first_name')
last_name = request.POST.get('last_name') # and so on...
Run Code Online (Sandbox Code Playgroud)
但是在这里我只使用了普通的请求对象 来填充我的views.py中的Form
因此,我必须创建一个视图来处理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 })
Run Code Online (Sandbox Code Playgroud)
如果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 })
Run Code Online (Sandbox Code Playgroud)
最后是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)
]
Run Code Online (Sandbox Code Playgroud)
整个事情可以得到改善和扩展,但首先它现在完成了所需的工作.
And the short summary: you can of course send input field data step by step to Django (using the same codes with little modifications), but at this particular Form it is absolutely unnecessary. The point of the task is: how to move Form tabs with Javascript, at the same time how to collect input data, and how to send Form data using AJAX to Django to handle/save Form input data to Django database. And at the same time we do not want page refresh.
And this screenshot shows the final Form visually: