async function stops while backend threadpoolexecutor is running - flask

I am trying to build a progress bar on my home page (index.html) using async function.
async function awaits for fetch from flask function #progress, and based on the fetched data, it updates the progress bar.
In flask, I have multithreaded API calls, which are initiated within #home function after the submit button is pressed from the index.html page. Submit button is part of a wtf form.
After succesful completion of threads, there is redirect to #output where the result is displayed in output.html.
This works perfectly in chrome browser in PC environment. However, in azure hosted environment, the async javascript function stops while the threadpoolexecutor is running in the backend. I cannot see the async function running in the log stream. The progress bar also stops updating since async has stopped.
Just before redirect for output, the last status is displayed in the progress bar.
#app.route('/', methods=['GET', 'POST'])
def home():
global TEMP_FILES
topic_form = TopicForm()
threads = []
if topic_form.validate_on_submit():
with ThreadPoolExecutor(max_workers=20) as executor:
for index, pdf_path in enumerate(TEMP_FILES):
threads.append(executor.submit(xxx, xxx,xxx))
wait(threads)
return redirect(url_for('output'))
return render_template("index.html", topic_form=topic_form)
#app.route('/progress/<int:file_id>')
def progress(file_id):
try:
status_dict = {'status': 'xyz, 'value':'123'}
except IndexError:
status_dict = {'status': "Start", 'value': "1"}
return json.dumps(status_dict)
**In the index.html**
<form method="POST">
{{ topic_form.csrf_token }}
{{ topic_form.submit }}
</form>
<p>The executions takes about 20 - 30 secs per file.</p>
<h5>File 1 - Progress Bar</h5>
<div class="progress progress-striped active">
<div id="file0-bar" class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 1%">Start</div>
</div>
**Script**
<script>
var timeout;
async function getStatus() {
let get_0;
try {
const res_0 = await fetch("/progress/0");
get_0 = await res_0.json();
} catch (e) {
console.error("Error: ", e);
}
document.getElementById("file0-bar").innerHTML = get_0.status;
document.getElementById("file0-bar").style.width= get_0.value + "%";
timeout = setTimeout(getStatus, 1000);
}
getStatus();
</script>

Related

FLask Blueprint and SocketIO not listening to events

I'm building a simple chat application with Flask and SocketIO. The user enters a username and a room key to join a room and start chatting.
In the beginning, I could make the sockets work to send and receive messages. However, at that moment I was not using any blueprint. Just templates and all the events and routes were in the same python file.
Later, I decided to split the application into folders and use blueprints. Mainly due to the incorporation of registration and authentication of users (No incorporated yet). However, the sockets stopped working but everything else is working as expected.
The directory tree looks as follows:
Chat_Rooms/
|
--- flask_chat_app/
| |
| --- static/css/
| --- templates/
|
| --- __init__.py
| --- auth.py
| --- events.py
| --- models.py
| --- views.py
|
--- app.py
My python and HTML files look as follows:
_init_.py
from flask import Flask
from flask_socketio import SocketIO
# create socket object
socket:SocketIO = SocketIO()
def create_app(debug=False):
"""Create application"""
app:Flask = Flask(__name__)
app.debug = debug
app.config['SECRET_KEY'] = 'mysecret!'
"""import and add blueprints to app"""
from .auth import auth
from .views import views
app.register_blueprint(views, url_prefix="/")
app.register_blueprint(auth, url_prefix="/")
"""Initialize socket"""
socket.init_app(app)
"""Return built app"""
return app
views.py
from flask import Blueprint
from flask import render_template, request
import random
# init blueprint object
views:Blueprint = Blueprint('views', __name__)
# Routes
"""
index
First page the user will see when enter to site
"""
#views.route("/")
def index():
return render_template("index.html")
"""
home:
Display options to generate key and join room
"""
#views.route("/home")
def home():
return render_template("home.html")
"""
generateKey:
Generate 2 random number between 100 - 1000. Put them together
to create a random key shown in the frontend.
Key is saved into a session as room-key
Increase room counter
"""
#views.route("/generate_key")
def generateKey():
# generate a random key
key1:int = random.randint(100,1000)
key2:int = random.randint(100,1000)
key:str = str(key1) + ' - ' + str(key2)
# render template with key
return render_template("generate_key.html", key=key)
"""
join_room_credentials:
Get username and room key. If it exist, enter room chat
"""
#views.route("/join_room", methods=['GET', 'POST'])
def join_room_credentials():
# get user information and enter chat room
if request.method == 'POST':
if request.form["username"] and request.form["roomKey"]:
# redirect to chat room page
return render_template("chat_room.html", username=request.form["username"], room=request.form["roomKey"])
# render html file to get credentials
else:
return render_template("join_room.html")
events.py
from .__init__ import socket
from flask_socketio import join_room
# socket routes
"""
handle_join_room:
event that will get the users in the same room and will notify to all clients
when a new user joins a room
"""
#socket.on("user_join_room")
def handle_join_room(data):
# print user and room in terminal
print("\n")
print(f"{data['username']} joined to room{data['room']}")
print("\n")
# add user to group
join_room(data["room"])
# send notification to clients
socket.emit('user_join_room', f"{data['username']} joined the room", to=data["room"])
"""
handle_message:
event to send a client message to all other clients
"""
#socket.on("user_message")
def handle_message(message):
socket.emit("user_message", f". {message['username']}: {message['message']} .", to=message["room"])
chat_room.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Chat Room</title>
<link rel="stylesheet" href="{{url_for("static", filename="CSS/chat_room.css")}}">
<!-- connect to server -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
<script>
// create socket to connect with server
const socket = io.connect("http://127.0.0.1:5000/")
/* --------- client to server ----------- */
// send message to server with user credentials
socket.on("connect", function() {
socket.emit('user_join_room', {
username: "{{username}}",
room: "{{room}}"
})
// get message text
let msgElem = document.getElementById("msg_input");
let msgForm = document.getElementById("mgs-form");
msgForm.onsubmit = function (event) {
event.preventDefault();
let msg = msgElem.value.trim();
if (msg.length) {
socket.emit("user_message", {
username: "{{username}}",
room: "{{room}}",
message: msg
})
}
msgElem.value = " ";
msgElem.focus();
}
});
/* ---------- server to client ------------ */
// add message when someone joins the room
socket.on("user_join_room", function(data) {
// create new paragraph
let newP = document.createElement("p");
let newNode = document.createTextNode(data);
newP.appendChild(newNode);
// get message div element
const msgDiv = document.getElementById("message");
msgDiv.appendChild(newP);
})
socket.on("user_message", function (data) {
let newP = document.createElement("p");
let newNode = document.createTextNode(" " + data + " ");
newP.appendChild(newNode);
// get message div element
const msgDiv = document.getElementById("message");
msgDiv.appendChild(newP);
})
</script>
</head>
<body>
<div id="chat-wrap">
<div id="header-wrap">
<h1>Room: {{room}}</h1>
</div>
<div id="chat-space">
<p id="message"></p>
</div>
<div id="msg-wrap">
<form id="mgs-form">
<input id="msg_input" placeholder="Enter message"></input>
<button type="submit">Send</button>
</form>
</div>
</div>
</body>
</html>
join_room.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Join Room</title>
<link rel="stylesheet" href="{{url_for("static", filename="CSS/join_room.css")}}">
</head>
<body>
<!-- Get username and room key -->
<div id="credential-wrap">
<form method="POST">
<div id="username_wrapper">
<label>Username: </label>
<input name="username" placeholder="frazzledcoal"></input>
</div>
<div id="key_wrapper">
<label>Room Key: </label>
<input name="roomKey" placeholder="xxx - xxx"></input>
</div>
<div id="button-wrap">
<button type="button">Back</button>
<button type="submit" id="submit-btn">Enter</button>
</div>
</form>
</div>
</body>
</html>
app.py
from flask_chat_app import create_app, socket
"""build application"""
chat_app = create_app(True)
if __name__ == "__main__":
socket.run(chat_app)
I tried to find a solution by looking at the Flask and SocketIO documentation but I still don't know why the sockets stopped communicating with each other.
I really would appreciate it if you guys can help me to solve this problem. This is my first stack overflow post, so I do apologize if any information is missing.
You have to import events.py somewhere for those event handlers to be seen by Flask-SocketIO. For example, in your create_app function:
"""import and add blueprints and events to app"""
from .auth import auth
from .views import views
from . import events

How to find which channel sent a particular message in Django channels + Websocket api?

I am implementing a broadcaster using Django channels i.e, i am implementing a group of channels where a message sent from a single consumer instance associated with a channel will be sent to all other instances that have their associated channels registered in that group.
I am using the template as following:
<!-- {% load static %} -->
<!DOCTYPE html>
<html lang="en">
<head>
<title>Messages</title>
<!-- <link href="{% static 'hello/styles.css' %}" rel="stylesheet"> -->
</head>
<body>
<textarea name="" id="chat-log" cols="100" rows="20"></textarea><br>
<input type="text" id="chat-message-input" size="100"><br>
<input type="button" value="send" id="chat-message-submit">
<script>
var ws = new WebSocket('ws://127.0.0.1:8000/ws/sc/')
ws.onopen = function(){
console.log('Websocket connection open....', event)
}
ws.onmessage = function(event){
const messageTextDom = document.getElementById('chat-log')
messageTextDom.value += JSON.parse(event['data']).msg + '\n'
console.log('Message Recieved From Serever...', event)
}
ws.onerror = function(event){
console.log('Message Error Occured...', event)
}
ws.onclose = function(event){
console.log('Webdsocket connection closed..', event)
}
document.getElementById('chat-message-submit').onclick =
function(event){
const messageInputDom = document.getElementById('chat-message-input')
const message = messageInputDom.value
ws.send(JSON.stringify({
'msg': message
}))
messageInputDom.value = ''
}
</script>
</body>
</html>
and have setup the group and message sending utility on the backend as:
from channels.generic.websocket import SyncConsumer
from asgiref.sync import async_to_sync
from channels.exceptions import StopConsumer
class TestConsumer(SyncConsumer):
def websocket_connect(self, event):
async_to_sync(self.channel_layer.group_add)('programmers', self.channel_name)
self.send({
'type': 'websocket.accept'
})
def websocket_receive(self, event):
async_to_sync(self.channel_layer.group_send)('programmers', {
'type': 'chat_message',
'message': event['text']
})
def chat_message(self, event):
self.send({
'type': 'websocket.send',
'text': event['message']
})
def websocket_disconnect(self, *args, **kwargs):
async_to_sync(self.channel_layer.group_discard)('programmers', self.channel_name)
raise StopConsumer()
How can i find which user sent the message and by comparing on the bases of user that sent the message perform some action
e.g, in whatsApp in a conversation in user1's view (consumer web instance) his messages gets displayed on the right side with green background,
while in user2's view (consumer web instance) his messages gets displayed on the right side with green background.

Flask-wtforms: Multiply two numbers from two different input fields and populate the result in third input field without refreshing the page

I'm working on a feature where if I input values to two different input fields then I should multiply and show into third input box without refreshing the page.
Example:-
Code:-
forms.py
class Multiply(FlaskForm):
number1 = IntegerField("number1")
number2 = IntegerField("number2")
result = IntegerField("result")
routes.py
#app.route('/multiply')
def multiply():
form = Multiply()
return render_template('multiply.html', form=form)
multiply.html
<form>
{{ form.number1.label }} {{ form.number1(class="form-control") }}
{{ form.number2.label }} {{ form.number2(class="form-control") }}
{{ form.result.label }} {{ form.result(class="form-control") }}
</form>
I'm new to this so I don't know how to do this.
You will find that this exact example is described in the Flask documentation. See the AJAX with jQuery example here.
The JSON view function:
from flask import Flask, jsonify, render_template, request
app = Flask(__name__)
#app.route('/_add_numbers')
def add_numbers():
a = request.args.get('a', 0, type=int)
b = request.args.get('b', 0, type=int)
return jsonify(result=a + b)
#app.route('/')
def index():
return render_template('index.html')
Html template:
<script type=text/javascript>
$(function() {
$('a#calculate').bind('click', function() {
$.getJSON($SCRIPT_ROOT + '/_add_numbers', {
a: $('input[name="a"]').val(),
b: $('input[name="b"]').val()
}, function(data) {
$("#result").text(data.result);
});
return false;
});
});
</script>
<h1>jQuery Example</h1>
<p><input type=text size=5 name=a> +
<input type=text size=5 name=b> =
<span id=result>?</span>
<p><a href=# id=calculate>calculate server side</a>
What you are trying to do is to update your view dynamically. HTML needs the help of javascript to dynamically update the page(in your case the result).
This can be done using an AJAX call to an API written on your backend which receives your two values as data and calculates the result and sends it back(possibly also storing it at the same time).
More info

How can I update the photo each second in my django template?

everyone. I am pretty new with django.
There is a particular position to show the photo in my django template, and the photo should be updated each second. How can I do that? (Paths for new photos come from the database. I know how to get them from db.)
I only know how to return a html with a url request, but how can I update a particular item in that html without a new url request?
More info.: The photos have to be shown are generated in real time by another process, so do the paths for the photos.
Actually, I do not implement it yet. I am still learning django, but I can simulate the situation by simple codes as follows:
urls.py:
urlpatterns = [ url(r'^hello/$', hello_world),]
views.py:
from django.shortcuts import render
from datetime import datetime
def hello_world(request):
return render(request, 'hello_world.html', {
'current_time': str(datetime.now()),
})
hello_world.html:
<!-- hello_world.html -->
<!DOCTYPE html>
<html>
<head>
<title>I come from template!!</title>
<style>
body {
background-color: lightyellow;
}
em {
color: LightSeaGreen;
}
</style>
</head>
<body>
<script>
setInterval(function() {
fetch("{% url 'hello_ajax' %}").then(function (response) {
var current_time = response.json().current_time;
console.log(current_time);
document.getElementById("test").innerHTML = current_time;
});
}, 1000);
</script>
<h1>Hello World!</h1>
<em id="test"></em>
</body>
</html>
We can update the current_time by refresh the page, but how about update the current_time each second without refresh the page? We can see the photo as the current_time to simulate my case.
Update:
It finally works:
<script>
setInterval(function() {
fetch("{% url 'hello_ajax' %}").then(response => response.json())
.then(data => {
// you can access your data here
document.getElementById("test").innerHTML = data.current_time;
});
}, 1000);
</script>
I am wondering why it is not working with:
<script>
setInterval(function() {
fetch("{% url 'hello_ajax' %}").then(response =>{
// you can access your data here
document.getElementById("test").innerHTML = response.json().current_time;
});
}, 1000);
</script>
it says that "(index):22 Uncaught (in promise) TypeError: Failed to execute 'json' on 'Response': body stream is locked at fetch.then.data"
Any idea?
Unfortunately, you can achieve it only with some JavaScript, and thing like setTimeout. If it won't be a big list, you could render it to the JavaScript array and then use e.g. setTimeout to switch the photo. It's not the best idea (I even remember reading some article with some valid points why it is a bad, will try to find it), but would do the work.
So, you could do something like this, at the end of body in the template:
<script>
arrayOfUrls = [
{% for url in urls}"{{ url }}",{% endfor %}
];
function switchPhoto(urlArray, switchTo) {
// if to go back to 0 if array finished
document.getElementById("your-photo-id").src = urlArray[switchTo];
setTimeout(switchPhoto, 1000, urlArray, switchTo + 1);
}
switchPhoto(arrayOfUrls, 0);
</script>
After update:
if you don't want to refresh the page, the solution would be probably the following:
views.py
from django.http import JsonResponse
def hello_world(request):
return render(request, 'hello_world.html', {
'current_time': str(datetime.now()),
})
def hellow_world_ajax(request):
return JsonResponse({'current_time': str(datetime.now())})
Add url(r'^hello_ajax/$', hellow_world_ajax, name="hello_ajax"), to urls.py.
And in the body of the template write JavaScript using e.g.: setInterval and either vanilla JS fetch or some js library / framework insert the periodic update logic:
<script>
setInterval(function() {
fetch("{% url 'hello_ajax' %}").then(function (response) {
document.getElementById("your-photo-id").src = response.json().current_time;
});
}, 1000);
</script>

jquery ajax / django - present form in a bootstrap modal and re-show if validation was not successful

my use case is:
a) Present a form loaded via ajax in a bootstrap modal, the fancy overlay effect stuff.. . I followed these instructions.
This works fine. (see code below)
b) Submit this form back to my Django app, try to validate it, and if it does not validate, re-show the form with the errors in the fancy bootstrap modal.
I can reload the form via ajax, but I m not able to represent it again in the modal.
Note: I did not include the view since it does nothing special. Only instantiating and validating the form.
Quite a lot to read below, so just continue if you think the use case sounds interesting...
My taskList.html looks like this:
<table id="listItemTable" class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Edit</th>
</tr>
</thead>
<tbody>
<tr>
<td>Task 1</td>
<td><a class="editItem" href="/update/item/1/">edit</a></td>
</tr>
</tbody>
</table>
<div class="modal hide" id="itemFormModal"></div>
<div id="modalExtraJsPlaceholder"></div>
.js for loading the form + showing the bootstrap modal + binding form to a .jquery submit call:
$(document).ready(function() {
modalConnect();
});
<script type="text/javascript">
//connects the modal load for each <a> with class editItem
//Functionality 1
//loads an item edit form from the server and replaces the itemFormModal with the form
//presents the modal with $("#itemFormModal").modal('show');
//Functionality 2
//loads some extra js "modalExtraJsHtml"
//calls the function "submitItemModalFormBind" which has been loaded via "modalExtraJsHtml"
function modalConnect(){
$(".editItem").click(function(ev) { // for each edit item <a>
ev.preventDefault(); // prevent navigation
url = ($(this)[0].href); //get the href from <a>
$.get(url, function(results){
var itemForm = $("#ajax_form_modal_result", results);
var modalExtraJs = $("#modalExtraJs", results);
//get the html content
var modalExtraJsHtml = modalExtraJs.html();
//update the dom with the received results
$('#itemFormModal').html(itemForm);
$('#modalExtraJsPlaceholder').html(modalExtraJsHtml);
$("#itemFormModal").modal('show');
submitItemModalFormBind(); //bind loaded form to ajax call
}, "html");
return false; // prevent the click propagation
})
}
</script>
The itemForm returned from the view looks like this:
<form id="#ajax_form_modal_result" class="well" method="post" action="/update/item/{{ item.id }}">
<div id="ajax_form_modal_result_div">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Edit Item</h3>
</div>
<div class="modal-body">
{% csrf_token %}
{{form.as_p}}
</div>
<div class="modal-footer">
<input class="btn btn-primary" type="submit" value="Save" />
<input name="cancel" class="btn" type="submit" value="Cancel"/>
</div>
</div>
</form>
Loading and showing the modal works fine.
But now comes the second part which does not work as expected. The issue is the following. If the form does not validates, the view returns the form. The returned form should be shown again in the bootstrap modal. But the result is that ONLY the form is presented in the browser, everything else is lost. No css, no table, only the form. Quite ugly.. Thus I did not achieve to update the ajax_form_modal_result_div. Can anyone help me out here what I m doing wrong..!?
The view returns also the js function 'submitItemModalFormBind' which prevents the form default behavior and sends the form via ajax.
<div id="modalExtraJs">
//ajax bind for update item form visualized via modal
function submitItemModalFormBind(){
var url = "{% url updateItem item.pk %}";
$('#ajax_form_modal_result').submit(function(){
$.ajax({
type: "POST",
url: "{% url updateTask item.pk %}",
data: $(this).serialize(),
success:function(response){
var div = $("ajax_form_modal_result_div", response);
$('#ajax_form_modal_result_div').html(div);
},
error: function (request, status, error) {
console.log("failure");
console.log(request.responseText);
}
});
});
return false;
}
</div>
Found a working approach (based upon this solution - and enhanced it with handling of invalid forms) and will post it for anybody who also want to use the stunning beautiful bootstrap modals with django. Major issue with the code above was that I did not correctly disabled the default behavior of the submit button and the approach for loading additional js was not a good idea. So I changed my strategy.
On documentReady or ajaxStop event bind the click event of the hyperlinks to the modalConnect function. Note that you only need the ajaxStop function if you have some kind of ajax which updates the content of your table (which I have):
<script type="text/javascript">
$(document).ready(function() {
modalConnect();
});
</script>
<script type="text/javascript">
$( document ).ajaxStop( function() {
modalConnect();
});
</script>
The modalConnect function which loads the form which we want to present in the modal and a formUpdateURLDiv:
<script type="text/javascript">
function modalConnect()
{
//unbind the click event. If not done we will end up with multiple click event bindings, since binding is done after each ajax call.
$(".editItem").unbind('click');
//bind the click event
$(".editItem").click(function(ev) { // for each edit item <a>
ev.preventDefault(); // prevent navigation
var url = this.href; //get the href from the <a> element
$.get(url, function(results){
//get the form
var itemForm = $("#ajax_form_modal_result", results);
//get the update URL
var formUpdateURLDiv = $("#formUpdateURL", results);
//get the inner html of the div
var formUpdateURL = formUpdateURLDiv.html();
//update the dom with the received form
$('#itemFormModal').html(itemForm);
//show the bootstrap modal
$("#itemFormModal").modal('show');
$(document).ready(function () {
//bind the form to an ajax call. ajax call will be set to the received update url
submitItemModalFormBind(formUpdateURL);
});
}, "html");
return false; // prevent the click propagation
})
}
</script>
the formUpdateURL includes a server generated (see included view below) url to which the loaded form has to make its form submission call. We use this url to "init" the submitItemModalFormBind function:
<script type="text/javascript">
function submitItemModalFormBind(url){
//bind the form. prevent default behavior and submit form via ajax instead
$('#ajax_form_modal_result').submit(function(ev){
$.ajax({
type: "POST",
url: url,
data: $(this).serialize(),
success:function(response, textStatus, jqXHR){
var form = $("#ajax_form_modal_result_div", response);
//form is returned if it is not valid. update modal with returned form
//change this "if" to check for a specific return code which should be set in the view
if (form.html()) {
console.log('Form was invalid and was returned');
//update modal div
$('#ajax_form_modal_result_div').html(form);
$("#itemFormModal").modal('show');
}
//form is not returned if form submission succeeded
else{
//update the entire document with the response received since we received a entire success page and we want to reload the entire page
document.open();
document.write(response);
document.close();
//sort by modified date descending
//var notificationDiv = $("#notification", response);
//$('#notification').html(notificationDiv.html());
console.log('Form was valid and was not returned');
$("#itemFormModal").modal('hide');
}
},
error: function (request, status, error) {
var div = $("ajax_form_modal_result_div", request.responseText);
$('#ajax_form_modal_result_div').html(div);
//implement proper error handling
console.log("failure");
console.log(request.responseText);
}
});
return false;
});
}
</script>
..and to see what is going on at the server see below the view which handles the logic:
class UpdateTaskModalView(LoginRequiredMixin, View):
template = 'list_management/crud/item/update_via_modal.html'
def get_logic(self, request, task_id, **kwargs):
task = get_object_or_404(Task.objects, pk=task_id)
task_form = TaskForm(instance=task)
context = {
'model_form': task_form,
'item': task,
}
return context
def post_logic(self, request, task_id, **kwargs):
task = get_object_or_404(Task.objects, pk=task_id)
task_form = TaskForm(request.POST, instance=task)
if task_form.is_valid():
task = task_form.save(commit=False)
task.modified_by = request.user
task.save()
messages.add_message(request, messages.INFO, 'Item "%s" successfully updated' % (task.name))
return ('redirect', HttpResponseRedirect(reverse('show_list_after_item_update', kwargs={'list_id':task.list.pk, 'item_id':task.pk})))
context = {
'model_form' : task_form,
'list': task.list,
'item': task,
}
return ('context', context)
def get(self, request, task_id, **kwargs):
context = self.get_logic(request, task_id, **kwargs)
return render_to_response(
self.template,
context,
context_instance = RequestContext(request),
)
def post(self, request, task_id, **kwargs):
post_logic_return = self.post_logic(request, task_id, **kwargs)
if post_logic_return[0] == 'redirect':
return post_logic_return[1]
if post_logic_return[0] == 'context':
context = post_logic_return[1]
return render_to_response(
self.template,
context,
context_instance = RequestContext(request),
)
..the form template is already included in my question: ajax_form_modal_result_div, you only have to provide also the formUpdateURL. I did it via the template, which seems quite odd now that I write this post. could be easily provided via the view context.
Voila - Django Forms with Bootstrap Modals! Spice up your UI!
I hope this helps somebody to solve a similar problem.
I wrote this simple AJAX that did the trick for me, hope it helps:
$(document).on('submit', 'div.modal-body form', function(e) {
var form_el = $(this);
e.preventDefault();
$.ajax({
type: $(this).attr('method'),
url: $(this).attr('action'),
data: $(this).serialize(),
success: function (xhr, ajaxOptions, thrownError) {
if ( $(xhr).find('.errorlist').length > 0 ) {
form_el.parents('.modal-body').html(xhr);
} else {
form_el.parents('.modal-body').html('<h4>Formulario enviado correctamente</h4>');
}
},
error: function (xhr, ajaxOptions, thrownError) {
form_el.parents('.modal-body').html(xhr);
}
});
});
Oh btw, you will also need something like this in order to load your form into the modal:
$('.modal-class').on('click',function(){
let dataURL = $(this).attr('data-href');
$('.modal-body').load(dataURL,function(){
$('#modal_crear').modal({show:true});
});
});