I have a django project using django-ckeditor. I use HTMX to create a bootstrap modal to show my edit form. It renders correctly (I did add the ckeditor.js files at the end of the body in base.html). If I call my form by going to localhost/1/update, change the RTF value, and click save, it works fine. But rendering it in the modal form and clicking save, the form.has_changed() returns false. If I edit another field and it saves, the value of RTF does NOT change. It seems like the POST does not include the changed value for the CKEditor field.
I've tried to reduce the code below to make it as short as possible.
My model:
class MyModel(models.Model):
name = models.CharField(max_length=50)
description = models.CharField(max_length=150)
comment = RichTextField(blank=True, null=True)
def __str__(self):
return f'{self.name} - {self.description}'
My form:
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
fields = (
'name',
'description',
'comment',
)
My View
def update_data(request, pk):
model = MyModel.objects.get(id=pk)
form = MyForm(request.POST or None, instance=model)
if request.method == "POST":
if form.is_valid():
print(form.has_changed())
form.save()
return redirect("detail-form", pk=model.id)
My HTML#1 - I click on the update button to open the modal
{% extends "layouts/base.html" %}
{% load static %}
{% block content %}
<main>
<div class="section section-md pt-4">
<div class="container">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th></th>
</tr>
</thead>
<tbody id="myforms">
{% for entry in entries %}
<tr id="entryform_{{entry.id}}">
<td>
<h4 class="h5">{{ entry.name }}</h4>
</td>
<td>
<h4 class="h5">{{ entry.description }}</h4>
</td>
<td style="width: 200px">
<button hx-get="{% url 'update-entry' entry.id %}" hx-swap="outerHTML"
class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-indigo-700 bg-indigo-100 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Update
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div id="modals-here"></div>
</main>
{% endblock content %}
{% block javascripts %}
<script>
function closeModal() {
var container = document.getElementById("modals-here")
var backdrop = document.getElementById("modal-backdrop")
var modal = document.getElementById("modal")
modal.classList.remove("show")
backdrop.classList.remove("show")
setTimeout(function() {
container.removeChild(backdrop)
container.removeChild(modal)
}, 200)
}
</script>
{% endblock javascripts %}
and then the last html, the model form. Notice that I did add the {{ form.media }}
<div id="modal-backdrop" class="modal-backdrop fade show" style="display:block;"> </div>
<div id="modal" class="modal fade show" tabindex="-1" style="display:block;">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-body">
<form method="post">
{% csrf_token %}
{{ form.media }}
{{ form.as_p|safe }}
<button type="submit" hx-post="{% url 'update-data' entry.id %}" hx-target="#entryform_{{entry.id}}" hx-swap="outerHTML"
class="btn btn-primary" onclick="closeModal()">
Submit
</button>
</form>
</div>
</div>
</div>
</div>
Any advice? My gut feel says it has something to do with the fact that the editor only gets loaded after the page has already loaded. If I recall I had a similar issue years ago with a JS date picker. I can't recall for the life of me how I fixed it then.
The problem was not with the modal code. HTMX caused the issue. This would always be the issue when using HTMX with RTF editors, and I think this solution would work for all cases. I found a similar issue with tinyMCE with a resolution for tinyMCE here. My implementation with CKEditor is the code as follows:
<div id="modal-backdrop" class="modal-backdrop fade show"
style="display:block;"> </div>
<div id="modal" class="modal fade show" tabindex="-1" style="display:block;">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-body">
<form method="post">
{% csrf_token %}
{{ form.media }}
<script>
document.body.addEventListener('htmx:configRequest', (event) => {
var element = new CKEDITOR.dom.element( document.getElementById( '{{ form.comment.id_for_label }}' ) );
event.detail.parameters['{{ form.comment.html_name }}'] = element.getEditor().getData();
})
</script>
{{ form.as_p|safe }}
<button type="submit" hx-post="{% url 'update-data' entry.id %}" hx-target="#entryform_{{entry.id}}" hx-swap="outerHTML"
class="btn btn-primary" onclick="closeModal()">
Submit
</button>
</form>
</div>
</div>
</div>
</div>
It might not be the best spot to put the script, but it works fine there. This spot also makes it possible to make it more generic using form tags.
Related
Problem
I want to the following things but am unable to understand that how can I do the following things.
Firstly, I want that when the page is loaded the defualt values should be displayed from using the value from the combox. For example pack of 1KG is the defualt value so it's price and other values should be updated when the page is loaded.
Secondly, I want that when the product is added to the cart the page is not reloaded or refreshed and a popup is shown that the product is added to the cart.
CODE
Script
$(document).on("change", '.tranactionID', function (event) {
event.preventDefault();
//get closest outer div..
var selector = $(this).closest(".productID")
//find to get required elements..
selector.find('.id_price').text($(this).children(":selected").attr("price"));
selector.find('.price-sale').text($(this).children(":selected").attr("sale_price"));
selector.find('.id_discount').text($(this).children(":selected").attr("discount"));
let id = $(this).find("option:selected").attr('transID');
let Url = `{% url 'cart:cart_add' 0 %}`.replace(0, id);
selector.find("form").attr('action', Url);
});
HTML
{% regroup transaction by productID as ProductList %}
{% for productID in ProductList %}
<div class="col-sm-3 productID" >
<div class="product">
<a href="{% url 'main:product-detail' productID.grouper.id %}" class="img-prod"><img class="img-fluid" src={{productID.grouper.product_image.url}} alt="" height="200px">
<span class="status id_discount">%</span>
<div class="overlay"></div>
</a>
<div class="text py-3 pb-4 px-3 text-center">
<h3>{{productID.grouper}}</h3>
<div class="d-flex">
<div class="pricing">
<p class="price"><span class="mr-2 price-dc id_price">Rs. </span><span class="price-sale">Rs. </span></p>
</div>
</div>
<select class="tranactionID" id="ItemID" style="width: 250px;">
{% for val in productID.list %}
<option transID={{val.id}} price={{val.Price}} discount={{val.discount_percentage}} sale_price={{val.get_sale}} class="price_value" >{{val.AUID}} - {{val.Description}}</option>
{% endfor %}
</select>
<form id='transactionIDValue' class="d-inline" method="post">
{{cart_product_form}}
{% csrf_token %}
<input type="submit" id="Id_submit" class="btn btn-primary shadow px-5 py-2" value="Add To Cart">
<!-- <button type="submit" class="btn btn-primary shadow px-5 py-2">Add to Cart</button> -->
</form>
</div>
</div>
</div>
{% endfor %}
In my Flask application, I have 10+ CRUD tables that I want to display one at a time using a Select dropdown. I have found and followed a lot of tutorials that helped me out in implementing one single CRUD table, but I haven't found a solution when it comes to the multiple tables.
In my current solution, I have managed to only make filtering and displaying tables work, but I am facing issues with the rest of the functionality like,
Unable to work the pagination, for any table other than default.
Setting up default table to be displayed
Editing/Deleting for specific tables
Rerouting back to the same table after editing/deleting
At this point, I had to hard code the table names in my code, but I want this all to be dynamic. How can I make it all work dynamically?
app.py
from flask import Flask, render_template, request, redirect, url_for, flash
from flask_sqlalchemy import SQLAlchemy
from forms import Dropdown_Form
app = Flask(__name__)
app.secret_key = "Secret Key"
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///site.db"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)
class Table_A(db.Model):
id = db.Column(db.Integer, primary_key=True)
code = db.Column(db.Integer)
name = db.Column(db.String(300))
def __init__(self, code, name):
self.code = code
self.name = name
class Table_B(db.Model):
id = db.Column(db.Integer, primary_key=True)
code = db.Column(db.Integer)
name = db.Column(db.String(300))
def __init__(self, code, name):
self.code = code
self.name = name
#app.route("/", methods=["GET", "POST"])
def index():
form = Dropdown_Form()
page = request.args.get("page", 1, type=int)
table = Table_A.query.paginate(page=page, per_page=5)
if request.method == "POST":
table = globals()[request.form.get("table_name")]
page = request.args.get("page", 1, type=int)
table = table.query.paginate(page=page, per_page=5)
return render_template("index.html", form=form, table=table)
return render_template("index.html", form=form, table=table)
#app.route("/insert", methods=["GET", "POST"])
def insert():
if request.method == "POST":
code = request.form.get("code")
name = request.form.get("name")
my_data = Table_B(code, name)
db.session.add(my_data)
db.session.commit()
flash("Data inserted successfully.")
return redirect(url_for("index"))
#app.route("/update", methods=["GET", "POST"])
def update():
if request.method == "POST":
my_data = Table_B.query.get(request.form.get("id"))
my_data.code = request.form.get("code")
my_data.name = request.form.get("name")
db.session.commit()
flash("Data updated successfully.")
return redirect(url_for("index"))
#app.route("/delete/<id>/", methods=["GET", "POST"])
def delete(id):
my_data = Table_B.query.get(id)
db.session.delete(my_data)
db.session.commit()
flash("Data deleted successfully.")
return redirect(url_for("index"))
if __name__ == "__main__":
db.create_all()
app.run(port=5000, debug=True)
forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, SelectField, SubmitField #, PasswordField, SubmitField, BooleanField
class Dropdown_Form(FlaskForm):
table_name = SelectField("Table Name", choices=[("Table_A","Table A"), ("Table_B", "Table B")])
submit = SubmitField("Submit")
templates\base.html
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
<meta charset="UTF-8">
<title>{% block title %} {% endblock %}</title>
</head>
<body>
{% block body %}
{% endblock %}
<!-- Option 1: Bootstrap Bundle with Popper -->
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js#1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.0.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4" crossorigin="anonymous"></script>
</body>
</html>
templates\index.html
{% extends 'base.html' %}
{% block title %} Home {% endblock %}
{% block body %}
<div class="container">
<div class="row mt-4 mb-4">
<div class="col md-12">
<div class="content-section">
<legend class="border-bottom mb-4">Manage Tables</legend>
<form method="POST" action="">
{{ form.hidden_tag() }}
<div class="row">
<div class="col-3">
{{ form.table_name(class="form-control form-select form-control-sm") }}
</div>
<div class="col-3">
{{ form.submit(class="btn btn-primary btn-sm btn-block") }}
</div>
</div>
</form>
<hr>
<div class="row">
<div class="col mt-2 mb-4">
<button type="button" class="btn btn-sm btn-primary float-left" data-toggle="modal" data-target="#mymodal">Add Data</button>
</div>
</div>
<table class="table table-sm table-hover">
<thead class="table-light">
<tr>
<th>Id</th>
<th>Code</th>
<th>Name</th>
<th>Action</th>
</tr>
</thead>
{% for row in table.items %}
<tr>
<td>{{ row.id }}</td>
<td>{{ row.code }}</td>
<td>{{ row.name }}</td>
<td>
Edit
Delete
</td>
</tr>
<!-- Modal Edit Employee-->
<div id="modaledit{{row.id}}" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Update Information</h4>
</div>
<div class="modal-body">
<form action="{{url_for('update')}}" method="POST">
<div class="form-group">
<label>Code:</label>
<input type="hidden" name="id" value="{{row.id}}" />
<input type="text" class="form-control" name="code" value="{{row.code}}" />
</div>
<div class="form-group">
<label>Name:</label>
<input type="text" class="form-control" name="name" value="{{row.name}}"/>
</div>
<div class="form-group">
<button class="btn btn-primary" type="submit">
Update
</button>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
{% endfor %}
</table>
{% for page in table.iter_pages() %}
{% if page %}
{% if table.page == page %}
<a class="btn btn-primary mb-4" href="{{ url_for('index', table_name='Table_A', page=page) }}">{{ page }}</a>
{% else %}
<a class="btn btn-outline-primary mb-4" href="{{ url_for('index', table_name='Table_A', page=page) }}">{{ page }}</a>
{% endif %}
{% endif %}
{% endfor %}
</div>
<!-- Modal Add Employee-->
<div id="mymodal" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Add Data</h4>
</div>
<div class="modal-body">
<form method="POST" action="{{url_for('insert')}}">
<div class="form-group">
<label>Code:</label>
<input type="text" class="form-control" name="code" required="1"/>
</div>
<div class="form-group">
<label>Name:</label>
<input type="text" class="form-control" name="name" required="1"/>
</div>
<div class="modal-footer">
<button class="btn btn-primary" type="submit">
Add Data
</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
Are you making a todo list app. if you want to implement things like searching for something in the table , pagination , or show these much entries then you can use datatables. it is a jquery library which will handle these functionalities.
Go and follow the instructions. It is super easy.
i know that there are a lot of posts with similar topic as this one, but none of them seem to be a useful fix in my case at least. As the headline describes i want to be able to send a new comment form to my database from a bootstrap modal. My data is being shown just fine so the only problem i got is that when i fill out the data for a new comment and press the submit button the modal is just being dismissed and no action has happened in my DB. My best guess for why it is not working is because i got my modal data in a separated html file detail.html. But otherwise i was not able to display the right data for each posts.
button to open the modal in home.html:
<a class="btn btn-outline-primary post_details-btn" class="open-modal" data-url="{% url 'post_detail' post.pk %}" width="17" height="17"><i class="fa fa-comments"></i></a>
my modal in home.html where i display the data from detail.html in the div with the id = modal-div
<div class="modal fade" id="myModal2">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">WINNER:</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="modal-div"></div>
</div>
</div>
</div>
</div>
my jquery to display the html code from detail.html in the modal, this code is also located in home.html
var modalDiv = $("#modal-div");
$(".post_details-btn").on("click", function() {
$.ajax({
url: $(this).attr("data-url"),
success: function(data) {
$('#myModal2').modal('show');
modalDiv.html(data);
}
});
});
detail.html
{% block content %}
{% load static %}
<div class="container">
<div class="row">
<div class="col-md-8 card mb-4 mt-3 ">
<div class="card-body">
<!-- comments -->
<h2>{{ comments.count }} comments</h2>
{% for comment in comments %}
<div class="comments" style="padding: 10px;">
<p class="font-weight-bold">
{{ comment.name }}
<span class=" text-muted font-weight-normal">
{{ comment.created_on }}
</span>
</p>
{{ comment.body | linebreaks }}
</div>
{% endfor %}
</div>
</div>
<div class="col-md-8 card mb-4 mt-3 ">
<div class="card-body">
{% if new_comment %}
<div class="alert alert-success" role="alert">
Your comment is awaiting moderation
</div>
{% else %}
<h3>Leave a comment</h3>
<form method="post" style="margin-top: 1.3em;">
{{ comment_form.as_p }}
{% csrf_token %}
<button type="submit" class="btn btn-primary submit">Submit</button>
</form>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock content %}
my post_detail function in views.py
def post_detail(request, pk):
allposts= Posts.objects.all()
alltags = Tag.objects.all()
post = get_object_or_404(Posts, pk=pk)
comments = post.comments.filter(active=True)
new_comment = None
# Comment posted
if request.method == 'POST':
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
# Create Comment object but don't save to database yet
new_comment = comment_form.save(commit=False)
# Assign the current post to the comment
new_comment.post = post
# Save the comment to the database
new_comment.save()
print("form is send!")
else:
comment_form = CommentForm()
context = {
'alltags': alltags,
'allposts': allposts,
'post': post,
'comments': comments,
'new_comment': new_comment,
'comment_form': comment_form,
'alltags': alltags,
}
return render(request, 'detail.html', context)
i did manage to get a solution myself after days of researching. The solution was to add the url in action attribute in the detail.html to the function in views.py that handles the comment form.
<form method="post" action="{% url 'post_detail' post.pk %}">
{{ comment_form.as_p }}
{% csrf_token %}
<button type="submit" class="btn btn-primary">submit</button>
</form>
I am new to programming and this is also my first question on this platform. I have tried implementing applicable solutions I could find here for similar questions raised but is still stuck. Should you believe that a previously answered question might answer mine, please share the link to such answer. Also please correct me should you find that I have broken some guidelines on how to properly ask questions on this platform.
Now, on to the actual issues.
The following are the relevant sections (please, let me know if I have missed something that might be relevant). The issues are stated on my comments on modals.js.
modals.js
$(document).ready(function(){
$('#frmAddProduct').on('submit', function aj (event) {
event.preventDefault();
$.ajax({
cache: false,
type : 'POST',
url : '/products/new',
data : $('#frmAddProduct').serialize(),
datatype: 'html'
})
.done(function process (data) {
/* The modal has to be shown unless the user clicks on 'close'*/
$('#modalAdd').modal('show');
let err = $(data).find(".invalid-feedback").html();
if (err){
/* replace the contents of the modal with section from
ajax result to show validation messages*/
let cont = $(data).find("#targetModalContent").html();
$('#targetModalContent').html(cont);
/* Once the code above executes, clicking the submit button again
terminates the whole process.
Maybe this section requires a loop or recursion? But this part
is already on the "on submit" listener, right?*/
}
else {
/* Show success flash message on the modal instead of the original
below nav location */
console.log(data);
let msg = $(data).closest("#targetFlash").html();
/* using the .find doesn't work and results in msg=undefined.
I don't understand why that is the case, I tried using .parent
but it also did not work, or maybe I was using the .parent improperly.
I also tried $($(data).closest("#targetFlash")).parent().html but
it too didn't worked.
Why is it returning only the message and not the whole node
with the enclosing div?
*/
$('#frmAddProduct fieldset>div:first').prepend(msg);
}
});
});
});
products.html
{% extends "tblayout.html" %}
<!-- Main Content -->
{% block thead %}
{% endblock %}
{% block tbody %}
{% endblock %}
{% block tfoot %}
{% endblock %}
{% block maincont %}
<!-- ModalAdd -->
<div class="modal fade" id="modalAdd" tabindex="-1" role="dialog" aria-labelledby="modalAddTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content" id="targetModalContent">
<div class="modal-header">
<h5 class="modal-title" id="modalAddLongTitle">Add {{ legend }}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<form method="POST" action="" id="frmAddProduct">
{{ formAdd.csrf_token }}
<div class="modal-body">
<div class="container-fluid">
<fieldset class="form-group">
<div class="form-group row">
{{ formAdd.productSubCategory.label(class="col-form-label col-sm-5") }}
{% if formAdd.productSubCategory.errors %}
{{ formAdd.productSubCategory(class="form-control col-sm-7 is-invalid") }}
<div class="invalid-feedback">
{% for error in formAdd.productSubCategory.errors %}
<span class="col-sm-7 offset-5">{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ formAdd.productSubCategory(class="form-control col-sm-7") }}
{% endif %}
</div>
<div class="form-group row">
{{ formAdd.brand.label(class="col-form-label col-sm-5") }}
{% if formAdd.brand.errors %}
{{ formAdd.brand(class="form-control col-sm-7 is-invalid") }}
<div class="invalid-feedback">
{% for error in formAdd.brand.errors %}
<span class="col-sm-7 offset-5">{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ formAdd.brand(class="form-control col-sm-7") }}
{% endif %}
</div>
<div class="form-group row">
{{ formAdd.description.label(class="col-form-label col-sm-5") }}
{% if formAdd.description.errors %}
{{ formAdd.description(class="form-control col-sm-7 is-invalid") }}
<div class="invalid-feedback">
{% for error in formAdd.description.errors %}
<span class="col-sm-7 offset-5">{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ formAdd.description(class="form-control col-sm-7") }}
{% endif %}
</div>
</fieldset>
</div>
</div>
<div class="modal-footer">
<div>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
<div>
{{ formAdd.submit(class="btn btn-primary") }}
</div>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
<!-- Optional Body scipt loaded after the main content has loaded -->
{% block bodyscript %}
<script src="../static/js/modals.js"></script>
{% endblock %}
AJAX result on Issue #1
<!-- ModalAdd -->
<div class="modal fade" id="modalAdd" tabindex="-1" role="dialog" aria-labelledby="modalAddTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
<div class="modal-content" id="targetModalContent">
<div class="modal-header">
<h5 class="modal-title" id="modalAddLongTitle">Add Product</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<form method="POST" action="" id="frmAddProduct">
<input id="csrf_token" name="csrf_token" type="hidden" value="ImNlZDc1Njc0MTY4NTg5NTNhNDg0NWEyNGYyZjYzZmIyYmFmMTZhZmQi.YAamUA.WGi03w__AklqccdIQgK_pWG5oJg">
<div class="modal-body">
<div class="container-fluid">
<fieldset class="form-group">
<div class="form-group row">
<label class="col-form-label col-sm-5" for="productSubCategory">Product Sub-Category</label>
<select class="form-control col-sm-7 is-invalid" id="productSubCategory" name="productSubCategory" required><option selected value="1">ProductCategory1, ProductSubCategory1</option><option value="2">ProductCategory1, ProductSubCategory2</option><option value="3">ProductCategory2, ProductSubCategory3</option></select>
<div class="invalid-feedback">
<span class="col-sm-7 offset-5">Product combination is already enrolled.</span>
</div>
</div>
<div class="form-group row">
<label class="col-form-label col-sm-5" for="brand">Brand</label>
<select class="form-control col-sm-7 is-invalid" id="brand" name="brand" required><option selected value="1">Brand1</option><option value="2">Brand2</option><option value="3">Brand3</option></select>
<div class="invalid-feedback">
<span class="col-sm-7 offset-5">Product combination is already enrolled.</span>
</div>
</div>
<div class="form-group row">
<label class="col-form-label col-sm-5" for="description">Product Description</label>
<input class="form-control col-sm-7 is-invalid" id="description" name="description" placeholder="New Product" required type="text" value="45">
<div class="invalid-feedback">
<span class="col-sm-7 offset-5">Product combination is already enrolled.</span>
</div>
</div>
</fieldset>
</div>
</div>
<div class="modal-footer">
<div>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
<div>
<input class="btn btn-primary" id="submit" name="submit" type="submit" value="Add Product">
</div>
</div>
</form>
</div>
</div>
</div>
AJAX result on Issue #2 (I have copied only the target div since the rest of the html data is just the same, this is right below the nav)
<div class="alert alert-success" id="targetFlash">
Product 45 has been added!
</div>
main.routes.py
#main.route("/products", methods=['GET', 'POST'])
#login_required
def products():
formAdd = frmAddProduct()
formMod = frmModProduct()
q = sess.query(Product.id.label("PId"),
Product.description.label("PDesc"),
Product.isactive.label("PIsactive"),
ProductSubCategory.id.label("PSCid"),
ProductCategory.description.label("PCDesc"),
ProductSubCategory.description.label("PSCDesc"),
Brand.id.label("BId"), Brand.name.label("BName"))\
.select_from(Product)\
.join(ProductSubCategory)\
.join(ProductCategory)\
.join(Brand)\
.all()
page = request.args.get('page', 1, type=int)
qp = paginate.Page(q, items_per_page=5, page=page)
data = sqlaq2dict(qp)
npage = qp.pager(url= url_for('main.products') + "?page=$page",
link_attr=dict({'class':'btn btn-outline-primary mb-4'}),
curpage_attr=dict({'class':'btn btn-primary mb-4'}),
dotdot_attr=dict())
pagenav = Markup(npage)
return render_template("products.html", title='Products', legend='Product',
data=data, pagenav=pagenav, formAdd=formAdd, formMod=formMod)
#main.route("/products/new", methods=['POST'])
#login_required
def addProduct():
formAdd = frmAddProduct()
if formAdd.validate_on_submit():
p = Product()
p.productSubCategory_id = formAdd.productSubCategory.data
p.brand_id = formAdd.brand.data
p.description = formAdd.description.data
p.isactive = formAdd.isactive.data
syncNext('management', 'Product', 'id') # Ensures right ID for Postgres
sess.add(p)
sess.commit()
flash(
f'Product {formAdd.description.data} has been added!', 'success')
# return jsonify({"status" : "success"}) #Cannot have multiple returns
return redirect(url_for('main.products'))
return render_template("products.html", title='Products', legend='Product',
formAdd=formAdd)
forms.py
class frmAddProduct(FlaskForm):
# productSubCategories = sess.query(ProductSubCategory.id,\
# ProductSubCategory.fullDesc)\
# .filter(ProductSubCategory.isactive==True)\
# .all()
psc = sess.query(ProductCategory.description.label("PCDesc"),
ProductSubCategory.id.label("PSCId"),
ProductSubCategory.description.label("PSCDesc"))\
.join(ProductSubCategory)\
.all()
pscDict = sqlaq2dict(psc) #Converts the resulting tuple to dict
productSubCategories = []
for r in pscDict:
i = (r['PSCId'], r['PCDesc'] + ', ' + r['PSCDesc'])
productSubCategories.append(i)
productSubCategory = SelectField('Product Sub-Category',
choices=productSubCategories,
coerce=int,
validators=[InputRequired()])
brands = sess.query(Brand.id, Brand.name)\
.filter(Brand.isactive == True)\
.all()
brand = SelectField('Brand', choices=brands, coerce=int,
validators=[InputRequired()])
description = StringField('Product Description', validators=[
DataRequired()], render_kw={"placeholder": "New Product"})
isactive = BooleanField('Status', default=True)
submit = SubmitField('Add Product')
#---------------------------------------------Should be composite Key
def validate_productSubCategory(self, productSubCategory):
user = sess.query(Product)\
.filter(Product.productSubCategory_id == self.productSubCategory.data,
Product.brand_id == self.brand.data, Product.description ==
self.description.data)\
.first()
if user:
raise ValidationError('Product combination is already enrolled.')
def validate_brand(self, brand):
user = sess.query(Product)\
.filter(Product.productSubCategory_id == self.productSubCategory.data,
Product.brand_id == self.brand.data, Product.description ==
self.description.data)\
.first()
if user:
raise ValidationError('Product combination is already enrolled.')
def validate_description(self, description):
user = sess.query(Product)\
.filter(Product.productSubCategory_id == self.productSubCategory.data,
Product.brand_id == self.brand.data, Product.description ==
self.description.data)\
.first()
if user:
raise ValidationError('Product combination is already enrolled.')
I'm working on an app where the user will submit a value and I want to hide the div containing the form on submit and display a div containing the results. The goal is to have them submit the form and display a different hidden div. What am I doing wrong with either the Django code or Javascript?
views.py
from django.shortcuts import render
from .models import VintageMac
from .forms import VintageMacForm
def home(request):
if request.method == "POST":
form = VintageMacForm(request.POST)
if form.is_valid():
form.save()
form = VintageMacForm()
else:
form = VintageMacForm()
return render(request, 'hmwypapp/index.html', {'form': form})
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='home'),
]
models.py
from django.conf import settings
from django.db import models
from django.utils import timezone
from django import forms
class VintageMac(models.Model):
price = models.IntegerField()
def publish(self):
self.save()
def __str__(self):
return '%s' % (self.price)
forms.py
from django import forms
from .models import VintageMac
class VintageMacForm(forms.ModelForm):
class Meta:
model = VintageMac
fields = ('price',)
HTML
<div id="submission1">
<p class="card-text">Some quick example text to build on the card title.</p>
<div class="bottom">
<div class="row">
<form action="/create_post/" class="w-100" method="POST" id="form1">
<div class="col-12">
{% csrf_token %}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field }}
{% endfor %}
{% for field in form.visible_fields %}
<div class="form-control">
{% render_field field min="0" class="form-control" id="amount1" placeholder="Type an amount." %}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
</div>
{% endfor %}
</div>
<div class="col-12">
<button onclick="myFunction1()" class="mt-1 text-center form-control btn submit-btn">Submit <span>🤑</span></button>
</div>
</form>
<div class="text-center credit mt-2 w-100">Submitted by #peterdenatale</div>
</div>
</div></div>
<div id="results1">
<p class="card-text">You said that you would pay <span class="value">$375.00</span> .</p>
<div class="bottom">
<div class="row">
<div class="col-12">
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="65" aria-valuemin="0" aria-valuemax="100" style="width: 65%;">
</div>
</div>
<div class="row" style="margin-bottom: -20px;">
<p class="col-4 raised-text"><strong>Min<br>$0.00</strong></p>
<p class="col-4 average-text"><strong>Avg<br>$250.00</strong></p>
<p class="col-4 goal-text"><strong>Max<br>$500.00</strong></p>
</div>
</div>
</div>
</div>
</div>
JavaScript
$(document).on('submit','#form1',function(e){
e.preventDefault();
$.ajax({
type:'POST',
url: '',
data:{
amount: $('#amount1').val(),
},
success:function(){
$('#submission1').fadeOut(500);
$('#results1').delay(500).fadeIn(500);
}
});
I tried changing the JavaScript to a bunch of different things, but the only thing that it does it reload the page. I want to get rid of the reload and stay on the same page but still post the data and hide/show the divs.
In line:
<button onclick="myFunction1()" class="mt-1 text-center form-control btn submit-btn">Submit <span>🤑</span></button>
Remove the onclick handler, add attribute type with value submit.
Updated line should look like:
<button type="submit" class="mt-1 text-center form-control btn submit-btn">Submit <span>🤑</span></button>
And update the url key in $.ajax options object to /create_post/ (from form tag action attribute)
1- I think you forgot to close the javascript tag that's why it doesn't work.
2- Change the submit button to be like this :
<input type="submit" class="mt-1 text-center form-control btn submit-btn" value="Submit"><span>🤑</span></div>
3- You forgot to add the csrf token in ajax as well , it should be like this :
csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val(),
Cheers :)