WTForms TextAreaField DataRequired not raising validation error in Bootstrap modal - python

I have a very small form in a bootstrap modal including just two fields: a TextAreaField and a Submit button. My TextAreField is DataRequired.
On top of that this field uses CKEditor (similar to quillsJS) to make the text area WYSIWYG.
Problem is that the form submits even when no data has been entered, and reloads the parent page.
I would very much like to know how to prevent that form submission and raise the validation error when no data has been entered.
I would like to get WTForm validation and not the browser's (which anyway doesn't work either for this case).
I should also mention, in case it matters, that my modal is a partial file (jinja).
My trigger for the modal is this, living in perfume-cursor.html
Edit
And this is my modal (in a partial file perfume-modals.html)
<div class="modal fade" id="reviewPerfumeModal" tabindex="-1" role="dialog" aria-labelledby="reviewPerfumeModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteUserModal">Please Leave a Review</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form method=POST action="{{ url_for('reviews.review_perfume', perfume_id=perfume._id) }}" id="form-review">
<fieldset class="form-group">
<div class="form-group">
{{ form.review(class="form-control form-control-md", placeholder="Review", id="description") }}
</div>
</fieldset>
<div class="modal-footer">
<div class="form-group">
{{ form.submit(class="btn btn-primary") }}
</div>
</div>
</form>
</div>
</div>
</div>
</div>
The id="description" is what CKEditor uses to render that text area field as a WYSIWYG editor, btw.
I would really appreciate a lot any help on this!!
Thanks in advance!
Update:
In case it helps this is my form class, although I'm pretty sure the issue is modal related and not WTF related:
class AddReviewForm(FlaskForm):
review = TextAreaField("Review", validators=[DataRequired()])
submit = SubmitField("Post Review")

Related

should django modal have a separate view?

I am working on an application that has a page with a table that is a list of users. Each user has a button which pops up a modal that does something. I am trying to create the modal that will get the user information when the button in clicked, additionally, I need in that modal some logic based on the user information and some more data. I am thinking that it makes sense to create a separate view for it but since each view must be on a different url, the concept of the modal does not makes sense (for me), because I want it to be on the same url.
Is it possible to have a modal with a different view that will have a separate logic? Since the modal is included in the template and it is basically just one template, then, can one template have 2 views?
What is the best way to implement the modal using django? And how to pass data to it, I need the data from the row that was selected + some more data from db. I have the data from the context but manipulating that using JQuery for me is a bad idea - because I am using a python session on the backend that communicates with another api, so when user clickes the modal save button, a python request needs to be done, this means I need a view for it.
Here is my try
Modal
{% block modal %}
{% load static %}
{% load authorization_tags %}
<!-- Modal -->
<div class="modal fade" id="{{modal_id}}" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="row">
<div class="col-lg-5">
<img class="d-block w-100"
src="{% static 'app/assets/images/security-shield1.png' %}"
alt="First slide">
Use access control to deauthorize user
</div>
<div class="col-lg-7">
<h2 class="h2-responsive product-name">
<strong>{{user.real_name}}</strong>
</h2>
<h4 class="h4-responsive">
<span class="green-text">
{{user.username}}
</span>
</span>
</h4>
<div class="accordion md-accordion" id="accordion1" role="tablist" aria-multiselectable="true">
<div class="card">
<div class="card-header" role="tab" id="heading2">
<a data-toggle="collapse" data-parent="#accordion1" href="#collapse1" aria-expanded="true"
aria-controls="collapse1">
<h5 class="mb-0">
Current Authorizations <i class="fas fa-angle-down rotate-icon"></i>
</h5>
</a>
</div>
<div id="collapse1" class="collapse show" role="tabpanel" aria-labelledby="heading1"
data-parent="#accordion1">
<div class="card-body">
{% for right in people_rights|user_active_rights:user.id %}
{{right.room}}
{% endfor %}
</div>
</div>
</div>
</div>
<div class="accordion md-accordion" id="accordion2" role="tablist" aria-multiselectable="true">
<div class="card">
<div class="card-header" role="tab" id="heading2">
<a data-toggle="collapse" data-parent="#accordion2" href="#collapse2" aria-expanded="true"
aria-controls="collapse2">
<h5 class="mb-0">
Available Authorizations <i class="fas fa-angle-down rotate-icon"></i>
</h5>
</a>
</div>
<div id="collapse2" class="collapse show" role="tabpanel" aria-labelledby="heading2"
data-parent="#accordion2">
<div class="card-body">
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="text-center">
<button type="button" class="btn btn-lg" data-dismiss="modal"><span>Close</span></button>
<button class="btn btn-lg"><span>Authorize</span></button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock modal %}
a piece of table and the modal included in the same template
{% include 'app/admin/users/user_authorize.html' with modal_id="authModal" user=row areas=areas rooms=rooms people_rights=people_rights %}
{%if row.empno %}
<td class="txt-oflo">{{ row.empno }}</td>
{% else %}
<td class="txt-oflo">-</td>
{% endif %}
<td>
{% with "#"|add:row.id as buttonId %}
<button id={{buttonId}} class="btn btn-lg" data-toggle="modal" data-target="#authModal">
<span>Authorize</span>
</button>
{% endwith %}
</td>
</tr>
You can try the following steps to solve the issue.
Create a separate view for the modal on a separate url.
Take the modal data out of this html file and place it in another html file. The empty modal in the original html should look something like this.
<div id="SampleTable" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">sample title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body" id="sample_datatable">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
Render that new html from the new view that was created. The ajax request that will be activated from the button that gives us the modal table might look something like this.
#app.route('/sample_data', methods=['GET', 'POST'])
def modal_table():
try:
data = request.args.get('arg')
database_data = Table.query.filter(Table.data == data).all()
result_dict = [u.__dict__ for u in database_data]
except Exception as e:
traceback.print_exc()
return render_template('modal_table.html', data=enumerate(result_dict))
The button in the original html that results in the modal table should be configured in a way using ajax requests that it renders data from the new html into the old modal data like this.
function get_sample_data() {
$.ajax({
url:"/sample_data", //the page containing python script
type: "GET", //request type,
data: {},
async:true,
beforeSend: function(){
$('#sample_datatable').html('Loading..');
},
success:function(result){
$('#sample_datatable').html(result);
}
});
}
You can call this javascript function from the button that is supposed to show the modal table.

Open New View in Bootstrap Modal in Django

My situation is that I have several modals (4 to be exact) which should be accessible in every single view in my app. I will use just one as an example. Let's take a look at how I am displaying this on the home screen view:
base.html
...abbreviated...
<a data-toggle="modal" href="#exampleModal">Shipment</buton>
<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Create Shipment</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<form method="POST">
{% csrf_token %}
<div class="modal-body">
{{shipmentForm|crispy}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" name="shipment" class="btn btn-primary">Save changes</button>
</form>
</div>
</div>
</div>
</div>
And here is the view which passes shipmentForm to that template
views.py
def HomeView(request):
if request.method == 'POST':
shipment_form = CreateShipmentForm(request.POST)
if shipment_form.is_valid():
new_shipment = shipment_form.save()the user to the success page
return redirect('ShipmentListView')
shipmentForm = CreateShipmentForm()
context = {
'shipmentForm': shipmentForm,
}
return render(request, 'home.html', context)
This works perfectly fine, but the problem is, I would need to include this code from HomeView in every single view in my app. That would be a lot of repeating myself. I have included the html for the modal in my base.html so that I don't have to repeat that. But instead of what I have above, I'd like to make the button which launches the modal look/do something like this.
base.html
<a data-toggle="modal" href="{% url 'CreateShipmentView' %}>Shipment</buton>
Put the html for the modal form in a separate file:
modal.html
<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Create Shipment</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<form method="POST">
{% csrf_token %}
<div class="modal-body">
{{shipmentForm|crispy}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" name="shipment" class="btn btn-primary">Save changes</button>
</form>
</div>
</div>
</div>
</div>
Then have a view like this:
def CreateShipmentView(request):
if request.method == 'POST':
shipment_form = CreateShipmentForm(request.POST)
if shipment_form.is_valid():
new_shipment = shipment_form.save()the user to the success page
return redirect('ShipmentListView')
shipmentForm = CreateShipmentForm()
context = {
'shipmentForm': shipmentForm,
}
return render(request, 'modal.html', context)
How is this possible without using django-modal-bootstrap package? I tried that but it seemed to be very very touchy and I did not like it so would prefer to avoid that. Can anyone point me in the direction of how I could make this work please?

Flask Bootstrap modal works on dev server, but not in production

Does anyone face this issue.. I have a very strange behavior with Flask and Bootstrap modal. On my dev server http://127.0.0.1:5000/ I have a modal which appears after user is typing wrong login credentials:
HTML code in Index page:
<div class="modal fade" id="ExistsModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">This email already exists!</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
If you have forgotten your password, .......
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Continue</button>
</div>
</div>
</div>
</div>
But on my production 'http://flask-env.xxx.us-west-1.elasticbeanstalk.com I can see the modal only if I manually refresh a page!
For return function I'm using
return redirect(url_for('index',_anchor='login')) and in index() I passed arguments!
{% if email_exists %}
<script>
$("#ExistsModal").modal('show');
</script>
{% endif %}
My question is - why modal show up after manual refresh on production? But on dev its works fine!

How to fix modals not showing?

I have a series of 6 modals that are created through a flask/jinja2 frame work loop and 1 modal that is an example outside of the loop. The modals all populate and populate correctly when looking at the page source. The example modal that is outside the loop shows without issue, however the other 6 modals will not fire.
https://www.sfiltrowani.pl/filter_instructions is the live site with the problem.
https://github.com/rscales02/sfiltrowani is the full code git repository.
This is for a flask app run on AWS elastic beanstalk. I was able to get the example modal to show by defining modal id and data target manually, those were the only changes I made from a copy/paste of the original modal code and original link to modals.
example link identical links from A-F
{{ _('(instrukcja photo F)') }}
modal loop to create image modals for instructions
{% for image in images %}
<!-- Modal -->
<div class="modal fade" id="{{ image }}" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Modal Header</h4>
</div>
<div class="modal-body">
<img id='modal-img' src="{{ url_for('static', filename='images/instructions/' + image) }}" alt="{{ loop.index }}">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
{% endfor %}
Expected results a modal that fires for each photo in my instructions. Current results only example modal will show.
[Edit]
I am new to the stack, edited to show the 3 lines of code that were hidden, due to lack of indentation. Thanks and sorry!
Your link does not point to the modal to show .
The modal needs an ID that is referenced by data-target
{{ _('(instrukcja photo F)') }}
<div class="modal-dialog" id="this-modal-id">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Modal Header</h4>
</div>
<div class="modal-body">
<img id='modal-img' src="{{ url_for('static', filename='images/instructions/' + image) }}" alt="{{ loop.index }}">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
I fixed it! It would appear that modal id's are sensitive to specific formatting and not just random strings. Previous version that did not work had id and data-targets of 'instrukcja*.jpg' when '.jpg' was removed, the modals function perfectly.

WTForms add additional categories to SelectField

I've seen forms like this on the internet before - basically I want to allow the user to dynamically add fields to a SelectField dropdown form in WTForms. For example, maybe the first field in the dropdown menu is a link that allows the user to add custom fields to the form. How would I go about implementing something like this in WTForms? Thanks!
Hi Michael I know this is 3 months late but I recently did something similar with the help of AngularJS and bootstrap modals so I thought I'll put this up for the sake of completeness:
This is a very simplified version I have not included error checking or validation of any sorts you of course want to do that for your production environment.
In my template the following is what the select field looks like. Instead of the first option being the one to enable a person to create something I just added a link next to the select field that allows a person to add a new category. The link itself opens up a form in a modal.
The following is the select field:
<div class="form-group">
{{ form.maincategory.label(class="col-sm-2 control-label") }}
<div class="col-sm-5">
{{ form.maincategory(**{'ng-model':'maincategory',
'ng-options':'c.id as c.displayname for c in allmaincategories',
'class':'form-control'}) }}
#over here is the link that opens the form in a modal window
</div><a data-toggle="modal" href="#newcategory">Add a new category</a>
</div>
When a user click on the link add new category the following modal window loads:
<!-- Add new category Modal -->
<div class="modal fade" id="newcategory" tabindex="-1" role="dialog"
aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="myModalLabel">Create new main category</h4>
</div>
<div class="modal-body">
<form method="post" ng-submit="createCategory()">
<div class="form-group">
{{ categoryform.displayname.label(class="col-sm-8 control-label") }}
<div>
{{ categoryform.displayname(**{'ng-model':'maincat.displayname','class':'form-control'}) }}
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<input type="submit" class="btn btn-primary" value="Save changes" />
</div>
</form>
</div>
</div>
</div>
</div>
The WTFORM form class that renders the above form in the modal window is as follows:
class DirectoryMainCategoryForm(Form):
displayname = TextField('Category name as it appears', validators=[DataRequired(message=(u'This category name is how it appears in the menu'))])
#the following is the form main form where the select appears
class MainEntry(Form):
maincategory = QuerySelectField('Main category', validators = DataRequired(message=(u'Please select a category for this place'))], get_label='category',
allow_blank=False, query_factory=Maincategory.query.all,blank_text =('-- Select a main category --'))
#And of course the javascript magic that glues it all together:
function DirectoryController($scope, $http){
#Adds a new category to the select menu
$scope.createCategory = function() {
var categorydata = angular.toJson($scope.maincat);
$http.post('/create/new/category', categorydata).success(function(data) {
$scope.allmaincategories = data.category;
});
}
}

Categories

Resources