django-forms-frontend-validation 1.0.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- django_forms_frontend_validation-1.0.0.dist-info/LICENSE +21 -0
- django_forms_frontend_validation-1.0.0.dist-info/METADATA +86 -0
- django_forms_frontend_validation-1.0.0.dist-info/RECORD +28 -0
- django_forms_frontend_validation-1.0.0.dist-info/WHEEL +5 -0
- django_forms_frontend_validation-1.0.0.dist-info/top_level.txt +1 -0
- formvalidator/__init__.py +0 -0
- formvalidator/admin.py +3 -0
- formvalidator/apps.py +6 -0
- formvalidator/form_utils/__init__.py +1 -0
- formvalidator/form_utils/form_utils.py +21 -0
- formvalidator/forms.py +9 -0
- formvalidator/migrations/__init__.py +0 -0
- formvalidator/models.py +3 -0
- formvalidator/settings.py +14 -0
- formvalidator/static/dist/forms.bundle.js +216 -0
- formvalidator/static/webpack/package-lock.json +2869 -0
- formvalidator/static/webpack/package.json +19 -0
- formvalidator/static/webpack/src/css/style.css +42 -0
- formvalidator/static/webpack/src/js/formFunctions.js +263 -0
- formvalidator/static/webpack/src/js/index.js +2 -0
- formvalidator/static/webpack/webpack.config.js +23 -0
- formvalidator/templates/base.html +26 -0
- formvalidator/templates/formvalidator/sample.html +51 -0
- formvalidator/templates/formvalidator/sample2.html +51 -0
- formvalidator/templates/includes/header.html +18 -0
- formvalidator/tests.py +21 -0
- formvalidator/urls.py +12 -0
- formvalidator/views.py +54 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
{
|
2
|
+
"name": "webpack",
|
3
|
+
"version": "1.0.0",
|
4
|
+
"description": "",
|
5
|
+
"private": true,
|
6
|
+
"scripts": {
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
8
|
+
"build": "webpack"
|
9
|
+
},
|
10
|
+
"keywords": [],
|
11
|
+
"author": "",
|
12
|
+
"license": "ISC",
|
13
|
+
"devDependencies": {
|
14
|
+
"css-loader": "^7.1.2",
|
15
|
+
"style-loader": "^4.0.0",
|
16
|
+
"webpack": "^5.97.1",
|
17
|
+
"webpack-cli": "^5.1.4"
|
18
|
+
}
|
19
|
+
}
|
@@ -0,0 +1,42 @@
|
|
1
|
+
/****** Forms ******/
|
2
|
+
input, select {
|
3
|
+
font-size: 18px!important;
|
4
|
+
}
|
5
|
+
|
6
|
+
.errorlist li, .error-msg {
|
7
|
+
color: red;
|
8
|
+
}
|
9
|
+
|
10
|
+
.error {
|
11
|
+
color: red;
|
12
|
+
font-style: italic;
|
13
|
+
}
|
14
|
+
|
15
|
+
.error-input {
|
16
|
+
background: #edbabf;
|
17
|
+
border-color: #dc3545;
|
18
|
+
}
|
19
|
+
|
20
|
+
.error-msg {
|
21
|
+
color: #dc3545;
|
22
|
+
margin-bottom: 0;
|
23
|
+
}
|
24
|
+
|
25
|
+
.success {
|
26
|
+
color: blue;
|
27
|
+
}
|
28
|
+
|
29
|
+
.readonly {
|
30
|
+
background-color: #e9ecef;
|
31
|
+
opacity: 1;
|
32
|
+
cursor: not-allowed;
|
33
|
+
}
|
34
|
+
|
35
|
+
.required-field {
|
36
|
+
font-weight: 800;
|
37
|
+
}
|
38
|
+
|
39
|
+
.required-field::after {
|
40
|
+
content: " *";
|
41
|
+
color: red;
|
42
|
+
}
|
@@ -0,0 +1,263 @@
|
|
1
|
+
// ########## Getting the csrf token for the fetch calls ##########
|
2
|
+
function getCookie(name) {
|
3
|
+
let cookieValue = null;
|
4
|
+
if (document.cookie && document.cookie !== '') {
|
5
|
+
const cookies = document.cookie.split(';');
|
6
|
+
for (let i = 0; i < cookies.length; i++) {
|
7
|
+
const cookie = cookies[i].trim();
|
8
|
+
// Does this cookie string begin with the name we want?
|
9
|
+
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
10
|
+
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
11
|
+
break;
|
12
|
+
}
|
13
|
+
}
|
14
|
+
}
|
15
|
+
return cookieValue;
|
16
|
+
}
|
17
|
+
export const csrftoken = getCookie('csrftoken');
|
18
|
+
|
19
|
+
// ##### Fetch Calls #####
|
20
|
+
// performs fetch call to view
|
21
|
+
export const fetchHandleForm = async (form) => {
|
22
|
+
let formData = new FormData(form);
|
23
|
+
return await fetch(form.action, {
|
24
|
+
method: "POST",
|
25
|
+
credentials: "same-origin",
|
26
|
+
headers: {
|
27
|
+
// "Accept": "m",
|
28
|
+
// "X-Requested-With": "XMLHttpRequest",
|
29
|
+
"X-CSRFToken": csrftoken,
|
30
|
+
},
|
31
|
+
body: formData,
|
32
|
+
}).then(async (response) => {
|
33
|
+
return response.json();
|
34
|
+
});
|
35
|
+
}
|
36
|
+
// ***** Adding asterisks to labels of required inputs *****
|
37
|
+
function addAsterisks(form) {
|
38
|
+
// gathering all inputs
|
39
|
+
let formGroups = document.querySelectorAll(`#${form.id} .form-group`);
|
40
|
+
let inputs = getRequiredFields(formGroups);
|
41
|
+
|
42
|
+
// adding the required-field class which will add the asterisk
|
43
|
+
for (let i = 0; i < inputs.length; i++) {
|
44
|
+
let label = document.querySelector(`label[for=${inputs[i].name}]`);
|
45
|
+
if (inputs[i].required) {
|
46
|
+
label.classList.add("required-field");
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
// In-line validation on required fields
|
52
|
+
function validateInputs(form) {
|
53
|
+
// gathering all inputs
|
54
|
+
let formGroups = document.querySelectorAll(`#${form.id} .form-group`);
|
55
|
+
let inputs = getRequiredFields(formGroups);
|
56
|
+
|
57
|
+
// adding listeners to each input for validation
|
58
|
+
for (let i = 0; i < inputs.length; i++) {
|
59
|
+
inputs[i].addEventListener("focusout", () => {
|
60
|
+
if (inputs[i].value === "" || inputs[i].value === null) {
|
61
|
+
addError(inputs[i]);
|
62
|
+
} else {
|
63
|
+
removeError(inputs[i]);
|
64
|
+
}
|
65
|
+
});
|
66
|
+
}
|
67
|
+
}
|
68
|
+
// validateInputs();
|
69
|
+
|
70
|
+
// check form function
|
71
|
+
export function checkForm(form) {
|
72
|
+
let errors = false;
|
73
|
+
|
74
|
+
// gathering all inputs
|
75
|
+
let formGroups = document.querySelectorAll(`#${form.id} .form-group`);
|
76
|
+
let inputs = getRequiredFields(formGroups);
|
77
|
+
|
78
|
+
// iterating through all required fields to check for invalidation
|
79
|
+
for (let i = 0; i < inputs.length; i++) {
|
80
|
+
let input = inputs[i];
|
81
|
+
if (input.value === "" || !input.value) {
|
82
|
+
addError(input);
|
83
|
+
errors = true;
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
return errors
|
88
|
+
}
|
89
|
+
|
90
|
+
// submit button validation check
|
91
|
+
function submitValidation(form) {
|
92
|
+
form.form.addEventListener("submit", (e) => {
|
93
|
+
// preventing default submission behavior
|
94
|
+
e.preventDefault();
|
95
|
+
|
96
|
+
// gathering all inputs
|
97
|
+
let f = e.target;
|
98
|
+
// let formGroups = document.querySelectorAll(`#${form.id} .form-group`);
|
99
|
+
// let inputs = getRequiredFields(formGroups);
|
100
|
+
// // let form = document.getElementById("form") !== null ? document.getElementById("form") : document.getElementById("userForm");
|
101
|
+
let errors = checkForm(f);
|
102
|
+
|
103
|
+
// submitting the form if there aren't any errors
|
104
|
+
if (!errors) {
|
105
|
+
form.form.submit();
|
106
|
+
} else {
|
107
|
+
let invalidInputs = document.getElementsByClassName("validation-error");
|
108
|
+
// scroll to the first invalid input
|
109
|
+
invalidInputs[0].parentElement.scrollIntoView();
|
110
|
+
}
|
111
|
+
});
|
112
|
+
}
|
113
|
+
|
114
|
+
// adds an error to an input
|
115
|
+
function addError(input) {
|
116
|
+
let inputParent = input.parentElement;
|
117
|
+
if (!inputParent.className.includes("form-group")){
|
118
|
+
inputParent = input.parentElement.parentElement;
|
119
|
+
}
|
120
|
+
let errorAdded = inputParent.dataset.errorAdded;
|
121
|
+
inputParent.classList.add("validation-error");
|
122
|
+
input.classList.add("error-input")
|
123
|
+
if (errorAdded === "false" || !errorAdded) {
|
124
|
+
// creating the error message p
|
125
|
+
let eP = document.createElement("p");
|
126
|
+
eP.setAttribute("class", "error-msg");
|
127
|
+
eP.innerText = "This input is required";
|
128
|
+
inputParent.appendChild(eP);
|
129
|
+
inputParent.dataset.errorAdded = "true";
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
// removes the error from an input
|
134
|
+
function removeError(input) {
|
135
|
+
let inputParent = input.parentElement;
|
136
|
+
if (!inputParent.className.includes("form-group")){
|
137
|
+
inputParent = input.parentElement.parentElement;
|
138
|
+
}
|
139
|
+
let errorAdded = inputParent.dataset.errorAdded;
|
140
|
+
inputParent.classList.remove("validation-error");
|
141
|
+
input.classList.remove("error-input");
|
142
|
+
// removing the error message p
|
143
|
+
if (errorAdded === "true") {
|
144
|
+
inputParent.removeChild(inputParent.lastElementChild);
|
145
|
+
inputParent.dataset.errorAdded = "false";
|
146
|
+
}
|
147
|
+
}
|
148
|
+
|
149
|
+
// function to get all required input
|
150
|
+
function getRequiredFields(formGroups) {
|
151
|
+
let nameList = ["SELECT", "INPUT", "TEXTAREA"];
|
152
|
+
let inputs = [];
|
153
|
+
// let formGroups = document.getElementsByClassName("form-group");
|
154
|
+
for (let i = 0; i < formGroups.length; i++) {
|
155
|
+
let children = formGroups[i].children;
|
156
|
+
for (let j = 0; j < children.length; j++) {
|
157
|
+
if (children[j].tagName === "DIV"){
|
158
|
+
let grandChildren = children[j].children;
|
159
|
+
for (let a = 0; a < grandChildren.length; a++) {
|
160
|
+
if (nameList.includes(grandChildren[a].tagName)) {
|
161
|
+
if (grandChildren[a].required) {
|
162
|
+
inputs.push(grandChildren[a]);
|
163
|
+
}
|
164
|
+
}
|
165
|
+
}
|
166
|
+
}
|
167
|
+
else{
|
168
|
+
if (nameList.includes(children[j].tagName)) {
|
169
|
+
if (children[j].required) {
|
170
|
+
inputs.push(children[j]);
|
171
|
+
}
|
172
|
+
}
|
173
|
+
}
|
174
|
+
}
|
175
|
+
}
|
176
|
+
return inputs
|
177
|
+
}
|
178
|
+
|
179
|
+
// checks if the form will be ignored form validation.
|
180
|
+
function getIgnored(form, ignoredClasses) {
|
181
|
+
let isIgnored = false;
|
182
|
+
let classes = form.classList;
|
183
|
+
if (ignoredClasses.length > 0) {
|
184
|
+
classes.forEach((_class) => {
|
185
|
+
if (ignoredClasses.includes(_class)) {
|
186
|
+
isIgnored = true;
|
187
|
+
return isIgnored;
|
188
|
+
}
|
189
|
+
});
|
190
|
+
}
|
191
|
+
return isIgnored
|
192
|
+
}
|
193
|
+
|
194
|
+
// Checks if the form will be validated.
|
195
|
+
function isValidate(form, ignoredClasses) {
|
196
|
+
let enableValidate = true;
|
197
|
+
let classes = form.classList;
|
198
|
+
if (ignoredClasses.length > 0) {
|
199
|
+
classes.forEach((_class) => {
|
200
|
+
if (ignoredClasses.includes(_class)) {
|
201
|
+
enableValidate = false;
|
202
|
+
return enableValidate;
|
203
|
+
}
|
204
|
+
});
|
205
|
+
}
|
206
|
+
return enableValidate
|
207
|
+
}
|
208
|
+
|
209
|
+
// Confirms if a form will only validate the form on submit, only.
|
210
|
+
function getValidateOnlyValidateOnSubmit(form, validateOnlyOnSubmit, enableValidation) {
|
211
|
+
let validateOnSubmit = false;
|
212
|
+
let trueFlags = ["all", "__all__", "*", "true"];
|
213
|
+
if (enableValidation) {
|
214
|
+
let classes = form.classList;
|
215
|
+
if (trueFlags.includes(validateOnlyOnSubmit[0])) {
|
216
|
+
validateOnSubmit = true;
|
217
|
+
return true;
|
218
|
+
}
|
219
|
+
else {
|
220
|
+
classes.forEach((_class) => {
|
221
|
+
if (validateOnlyOnSubmit.includes(_class)) {
|
222
|
+
validateOnSubmit = true;
|
223
|
+
return validateOnlyOnSubmit;
|
224
|
+
}
|
225
|
+
});
|
226
|
+
}
|
227
|
+
}
|
228
|
+
return validateOnSubmit;
|
229
|
+
}
|
230
|
+
|
231
|
+
// adds listener logic to the form
|
232
|
+
export function _Initialize(form={}){
|
233
|
+
if (!form.ignored || form.enableValidation) {
|
234
|
+
// add all listeners to each form
|
235
|
+
addAsterisks(form);
|
236
|
+
if (!form.validateOnlyOnSubmit) {
|
237
|
+
validateInputs(form);
|
238
|
+
}
|
239
|
+
if (!form.ignored) {
|
240
|
+
submitValidation(form);
|
241
|
+
}
|
242
|
+
}
|
243
|
+
}
|
244
|
+
|
245
|
+
// function to initialize forms into a json object
|
246
|
+
export function _InitializeForms(formNodes, ignoredFormClasses=[], ignoredValidationClasses=[], validateOnlyOnSubmit){
|
247
|
+
for (let i = 0; i < formNodes.length; i++) {
|
248
|
+
let currentForm = formNodes[i];
|
249
|
+
let ignored = getIgnored(currentForm, ignoredFormClasses);
|
250
|
+
let enableValidation = isValidate(currentForm, ignoredValidationClasses);
|
251
|
+
let validateOnSubmit = getValidateOnlyValidateOnSubmit(currentForm, validateOnlyOnSubmit, enableValidation);
|
252
|
+
let _form = {
|
253
|
+
id: currentForm.id,
|
254
|
+
form: currentForm,
|
255
|
+
ignored: ignored,
|
256
|
+
enableValidation: enableValidation,
|
257
|
+
validateOnlyOnSubmit: validateOnSubmit,
|
258
|
+
}
|
259
|
+
|
260
|
+
// adding form functionality
|
261
|
+
_Initialize(_form);
|
262
|
+
}
|
263
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
const path = require("path");
|
2
|
+
|
3
|
+
module.exports = {
|
4
|
+
mode: "development",
|
5
|
+
entry: {
|
6
|
+
forms: ["./src/css/style.css", "./src/js/formFunctions.js",],
|
7
|
+
},
|
8
|
+
output: {
|
9
|
+
filename: "[name].bundle.js",
|
10
|
+
path: path.resolve(__dirname, "../dist"),
|
11
|
+
library: "fv",
|
12
|
+
libraryTarget: "umd",
|
13
|
+
globalObject: "this",
|
14
|
+
},
|
15
|
+
module: {
|
16
|
+
rules: [
|
17
|
+
{
|
18
|
+
test: /\.css/i,
|
19
|
+
use: ["style-loader", "css-loader"],
|
20
|
+
},
|
21
|
+
],
|
22
|
+
},
|
23
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
{% load static %}
|
3
|
+
<html lang="en">
|
4
|
+
<head>
|
5
|
+
<meta charset="UTF-8">
|
6
|
+
<title>{% block title %}{{ title }}{% endblock %}</title>
|
7
|
+
|
8
|
+
<!-- Bootstrap CDNs -->
|
9
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
10
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
11
|
+
|
12
|
+
{% block extra_head %}{% endblock %}
|
13
|
+
</head>
|
14
|
+
<body>
|
15
|
+
<!-- header -->
|
16
|
+
{% include 'includes/header.html' %}
|
17
|
+
|
18
|
+
<!-- main content -->
|
19
|
+
<div class="container my-3">
|
20
|
+
{% block content %}{% endblock %}
|
21
|
+
</div>
|
22
|
+
|
23
|
+
{% block extra_js %}{% endblock %}
|
24
|
+
|
25
|
+
</body>
|
26
|
+
</html>
|
@@ -0,0 +1,51 @@
|
|
1
|
+
{% extends 'base.html' %}
|
2
|
+
{% load static %}
|
3
|
+
|
4
|
+
{% block content %}
|
5
|
+
<h3>Sample Form</h3>
|
6
|
+
<hr>
|
7
|
+
<p>
|
8
|
+
Sample form where forms.bundle.js is being added via an HTML <code><script></code> tag.
|
9
|
+
Then, the object variable, <code>fv</code>, is being exported and used within another <code><script></code> tag.
|
10
|
+
</p>
|
11
|
+
<div id="formDiv">
|
12
|
+
<form id="sampleForm" action="{% url 'sample-form' %}" method="post" novalidate>
|
13
|
+
{% csrf_token %}
|
14
|
+
{% if form.non_field_errors %}
|
15
|
+
{% for error in form.non_field_errors %}
|
16
|
+
{{ error }}
|
17
|
+
{% endfor %}
|
18
|
+
{% endif %}
|
19
|
+
|
20
|
+
{% for field in form %}
|
21
|
+
<div class="form-group mb-3">
|
22
|
+
<label class="form-label" for="{{ field.name }}">{{ field.label }}</label>
|
23
|
+
{{ field }}
|
24
|
+
{% if field.errors %}
|
25
|
+
{{ field.errors }}
|
26
|
+
{% endif %}
|
27
|
+
</div>
|
28
|
+
{% endfor %}
|
29
|
+
|
30
|
+
<button class="btn btn-primary btn-lg" type="submit">Submit</button>
|
31
|
+
</form>
|
32
|
+
</div>
|
33
|
+
{% endblock %}
|
34
|
+
|
35
|
+
{% block extra_js %}
|
36
|
+
<script src="{% static 'dist/forms.bundle.js' %}"></script>
|
37
|
+
<script>
|
38
|
+
// fv is exported from forms.bundle.js
|
39
|
+
window.addEventListener("load", () => {
|
40
|
+
let ignoredClasses = []; // add more classes that represent forms you want this script to ignore.
|
41
|
+
let ignoreValidation = []; // add any form classes that you want to ignore validation
|
42
|
+
let validateOnlyOnSubmit = []; // for hitting all forms make index 0 either __all__, all, * or leave blank for false or use false
|
43
|
+
let forms = document.getElementsByTagName("form");
|
44
|
+
// if (form || userForm) {
|
45
|
+
if (forms.length > 0) {
|
46
|
+
// calling specific functions on all forms
|
47
|
+
fv._InitializeForms(forms, ignoredClasses, ignoreValidation, validateOnlyOnSubmit);
|
48
|
+
}
|
49
|
+
});
|
50
|
+
</script>
|
51
|
+
{% endblock %}
|
@@ -0,0 +1,51 @@
|
|
1
|
+
{% extends 'base.html' %}
|
2
|
+
{% load static %}
|
3
|
+
|
4
|
+
{% block extra_head %}
|
5
|
+
<script src="https://assets.tucsonaz.gov/share/web/js/forms.bundle.js" crossorigin="anonymous"></script>
|
6
|
+
{% endblock %}
|
7
|
+
|
8
|
+
{% block content %}
|
9
|
+
<h3>Sample Form 2</h3>
|
10
|
+
<hr>
|
11
|
+
<p>This form only validates when the submit button is clicked.</p>
|
12
|
+
<div id="formDiv">
|
13
|
+
<form id="sampleForm" class="sample-form2" action="{% url 'sample-form2' %}" method="post" novalidate>
|
14
|
+
{% csrf_token %}
|
15
|
+
{% if form.non_field_errors %}
|
16
|
+
{% for error in form.non_field_errors %}
|
17
|
+
{{ error }}
|
18
|
+
{% endfor %}
|
19
|
+
{% endif %}
|
20
|
+
|
21
|
+
{% for field in form %}
|
22
|
+
<div class="form-group mb-3">
|
23
|
+
<label class="form-label" for="{{ field.name }}">{{ field.label }}</label>
|
24
|
+
{{ field }}
|
25
|
+
{% if field.errors %}
|
26
|
+
{{ field.errors }}
|
27
|
+
{% endif %}
|
28
|
+
</div>
|
29
|
+
{% endfor %}
|
30
|
+
|
31
|
+
<button class="btn btn-primary btn-lg" type="submit">Submit</button>
|
32
|
+
</form>
|
33
|
+
</div>
|
34
|
+
{% endblock %}
|
35
|
+
|
36
|
+
{% block extra_js %}
|
37
|
+
<script>
|
38
|
+
// fv (formsvalidator) is exported from forms.bundle.js
|
39
|
+
window.addEventListener("load", () => {
|
40
|
+
let ignoredClasses = {{ form_validator.ignored_classes|safe }}; // add more classes that represent forms you want this script to ignore.
|
41
|
+
let ignoreValidation = {{ form_validator.ignore_validation|safe }}; // add any form classes that you want to ignore validation
|
42
|
+
let validateOnlyOnSubmit = {{ form_validator.validate_only_on_submit|safe }}; // for hitting all forms make index 0 either __all__, all, * or leave blank for false or use false
|
43
|
+
let forms = document.getElementsByTagName("form");
|
44
|
+
// if (form || userForm) {
|
45
|
+
if (forms.length > 0) {
|
46
|
+
// calling specific functions on all forms
|
47
|
+
fv._InitializeForms(forms, ignoredClasses, ignoreValidation, validateOnlyOnSubmit);
|
48
|
+
}
|
49
|
+
});
|
50
|
+
</script>
|
51
|
+
{% endblock %}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
2
|
+
<div class="container">
|
3
|
+
<a class="navbar-brand" href="{% url 'sample-form' %}">Form Validator</a>
|
4
|
+
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
5
|
+
<span class="navbar-toggler-icon"></span>
|
6
|
+
</button>
|
7
|
+
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
8
|
+
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
9
|
+
<li class="nav-item">
|
10
|
+
<a class="nav-link" href="{% url 'sample-form' %}">Sample Form</a>
|
11
|
+
</li>
|
12
|
+
<li class="nav-item">
|
13
|
+
<a class="nav-link" href="{% url 'sample-form2' %}">Sample Form 2</a>
|
14
|
+
</li>
|
15
|
+
</ul>
|
16
|
+
</div>
|
17
|
+
</div>
|
18
|
+
</nav>
|
formvalidator/tests.py
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
from django.test import TestCase
|
2
|
+
|
3
|
+
from .form_utils.form_utils import FormsValidator
|
4
|
+
|
5
|
+
|
6
|
+
# Create your tests here.
|
7
|
+
class GetContextTest(TestCase):
|
8
|
+
def setUp(self):
|
9
|
+
self.form_validator = FormsValidator()
|
10
|
+
self.context = self.form_validator.get_context()
|
11
|
+
|
12
|
+
def test_get_context(self):
|
13
|
+
"""Ensuring that the context variable is set correctly"""
|
14
|
+
self.assertEqual(type(self.context), dict, msg="The context variable is not a dictionary datatype")
|
15
|
+
|
16
|
+
def test_context_variables(self):
|
17
|
+
"""Tests that the context variables are set correctly"""
|
18
|
+
context_keys = self.context.keys()
|
19
|
+
correct_keys = ["ignored_classes", "ignore_validation", "validate_only_on_submit"]
|
20
|
+
for key in context_keys:
|
21
|
+
self.assertTrue(key in correct_keys, msg=f"The context variable {key} is missing")
|
formvalidator/urls.py
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
from django.urls import path
|
2
|
+
from django.conf import settings
|
3
|
+
|
4
|
+
from . import views
|
5
|
+
|
6
|
+
urlpatterns = []
|
7
|
+
|
8
|
+
if settings.DEBUG:
|
9
|
+
urlpatterns += [
|
10
|
+
path('demo/sample-form/', views.sample, name='sample-form'),
|
11
|
+
path('demo/sample-form2/', views.sample2, name='sample-form2'),
|
12
|
+
]
|
formvalidator/views.py
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
from django.shortcuts import render
|
2
|
+
|
3
|
+
from .forms import SampleForm
|
4
|
+
|
5
|
+
from .form_utils import FormsValidator
|
6
|
+
|
7
|
+
|
8
|
+
# Create your views here.
|
9
|
+
def sample(request):
|
10
|
+
"""
|
11
|
+
Sample form view for demonstration purposes only.
|
12
|
+
Will provide an example of using the forms.bundle.js file as an imported module.
|
13
|
+
Will also not use the settings.py variables.
|
14
|
+
"""
|
15
|
+
title = "Form Validator Sample"
|
16
|
+
template = "formvalidator/sample.html"
|
17
|
+
|
18
|
+
if request.method == 'POST':
|
19
|
+
form = SampleForm(request.POST)
|
20
|
+
if form.is_valid():
|
21
|
+
pass
|
22
|
+
else:
|
23
|
+
form = SampleForm()
|
24
|
+
|
25
|
+
context = {
|
26
|
+
"form": form,
|
27
|
+
"title": title,
|
28
|
+
}
|
29
|
+
return render(request, template, context)
|
30
|
+
|
31
|
+
|
32
|
+
def sample2(request):
|
33
|
+
"""
|
34
|
+
Another sample form view for demonstration purposes only.
|
35
|
+
This demo will show how to use the forms.bundle.js file as a CDN, and using the settings variables
|
36
|
+
for the configuration.
|
37
|
+
"""
|
38
|
+
title = "Form Validator Sample 2"
|
39
|
+
template = "formvalidator/sample2.html"
|
40
|
+
form_validator = FormsValidator()
|
41
|
+
|
42
|
+
if request.method == 'POST':
|
43
|
+
form = SampleForm(request.POST)
|
44
|
+
if form.is_valid():
|
45
|
+
pass
|
46
|
+
else:
|
47
|
+
form = SampleForm()
|
48
|
+
|
49
|
+
context = {
|
50
|
+
"form": form,
|
51
|
+
"title": title,
|
52
|
+
"form_validator": form_validator.get_context(),
|
53
|
+
}
|
54
|
+
return render(request, template, context)
|