so the basic goal of my project is to allow teachers and admin to login to the system and take attendance for the students. my new issue is that i have a view function that is suppose to check to see if students student is already added to the respective class and if they are it will exclude them from the drop down menu and display the students who aren't in the class but it not doing that instead it is showing me a blank drop down menu. the way i have it set up is that the admin clicks the add student button then it will convert a .html page to modal which brings up the list of students to select from. When i add the form responsible for saving the student to the class in the Django admin it work perfect but i cant seem to get it to work in the user section Ui. I am new to Django and this is my first project so any help is really appreciated
Views
#check to see if students already exist in class and display those who aint to be added
#login_required
def manage_class_student(request, classPK=None):
if classPK is None:
messages.warning(request,'Class ID is Unknown')
else:
context['classPK'] = classPK
_class = Room.objects.get(id=classPK)
# print(ClassStudent.objects.filter(classIns = _class))
students = Student.objects.exclude(id__in=ClassStudent.objects.filter(classIns=_class).values_list('student').distinct()).all()
context['students'] = students
return render(request, 'manage_class_student.html', context)
#save the students to the respective class after being selected
#login_required
def save_class_student(request):
form = SaveClassStudent()
if request.method == 'POST':
form = SaveClassStudent(request.POST)
if form.is_valid():
form.save()
messages.success(request, "Student has been added successfully.")
redirect('class_detail')
else:
messages.error(request, 'Error Adding student in class')
template with the form to show the available students
<!-- Modal -->
<div class="modal fade" {% block modal-id %} id="addModal{{class.pk}}" {% endblock%} data-bs-backdrop="static"
data-bs-keyboard="false" tabindex="-1" aria-labelledby="addModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h6 class="modal-title" id="addModalLabel"> {% block modal-title%}<i class="fas fa-plus"></i> Add
Student To Class{% endblock%}</h6>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="container-fluid">
<form {% block action %} action="{% url 'save-class-student' %}" {% endblock%} method="post">
{% csrf_token %}
<div class="modal-body">
<input type="hidden" name="classIns" value="{{ classPK }}">
<div class="form-group mb-3">
<label for="student" class="control-label">Student</label>
<select name="student" id="student" class="form-select rounded-0 select2" required>
<option disabled selected></option>
{% for student in students %}
<option value="{{ student.id }}">{{ student }}</option>
{% endfor %}
</select>
</div>
<div>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
</div>
</div>
</div>
</div>
What triggers the modal
<div class="tools">
<button type="button" class="btn btn-secondary border rounded-0 bg-gradient btn-sm"
id='print_attendance_report'><i class="fa fa-print"></i> Print Attendance Report</button>
<a data-bs-toggle="modal" data-bs-target="#addModal{{class.pk}}"> <button type="button"
class="btn btn-primary rounded-0 bg-gradient btn-sm" id='add_new'><i class="fa fa-plus"></i>
Add Student</button></a>
</div>
form that save students to classroom
class SaveClassStudent(forms.ModelForm):
classIns = forms.IntegerField()
student = forms.IntegerField()
class Meta:
model = ClassStudent
fields = ('classIns', 'student')
def clean_classIns(self):
cid = self.cleaned_data['classIns']
try:
classIns = Room.objects.get(id=cid)
return classIns
except:
raise forms.ValidationError("Class ID is Invalid.")
def clean_student(self):
student_id = self.cleaned_data['student']
_class = Room.objects.get(id=self.data.get('classIns'))
student = Student.objects.get(id=student_id)
try:
cs = ClassStudent.objects.get(classIns=_class, student=student)
if len(cs) > 0:
raise forms.ValidationError(
f"Student already exists in the Class List.")
except:
return student
Related
This question has “grown” from Django Model Field for html5 Range Slider but is specific and different enough to warrant its own question, I believe, rather than continue in the comments section.
The aim of the original post was to write Django code that would render and display a slider in the webpage. That has been done. The challenge that this question relates to is writing the value of the slider to a field in a table/model. I will first show code for a table/model update that works then show how far I have got displaying the slider and showing where I am stuck. I am giving all this detail rather than just asking for a fix, because I am trying to understand how the Django code works - and by trying to explain it hopefully you can correct my misunderstandings.
First I will show the code I have copied and modified that just displays textboxes that the View(?) form(?) takes the input values and write a new record to the table. I will describe my understanding of the various parts of the view and the form.
Then I will show the code I have to display the slider and how it differs from the working code – which is where I need help to write data to the table/model.
My template, create.html, that works contains:
<form method="POST">
{% csrf_token %}
<div class="panel-body">
<div class="row">
<div class="col-lg-12">
<div class="form-group">
<label for="question">Enter Survey Question</label>
{% render_field form.q_text rows="3" class="form-control" %}
</div>
</div>
</div>
<div class="row">
<div class="col-lg-5">
<div class="form-group">
<label for="Choice1_text ">Choice 1</label>
{% render_field form.Choice1_text class="form-control" %}
<label for="C1_type">Type 1</label>
{% render_field form.C1_Type class="form-control" %}
</div>
</div>
<div class="col-lg-5">
<div class="form-group">
<label for="option2">Choice 2</label>
{% render_field form.Choice2_text class="form-control" %}
<label for="C2_type">Type 2</label>
{% render_field form.C2_Type class="form-control" %}
</div>
</div>
</div>
<div class="row">
<hr />
<div class="col-lg-4">
<button type="submit" class="btn btn-info">Submit</button>
</div>
</div>
</div>
</form>
My understanding the form will be “interpreted” by the view as a POST.
The input fields which appear on the rendered webpage as input boxes, are coded in the template as in the following examples:
<label for="question">Enter Survey Question</label>
<label for="Choice1_text ">Choice 1</label>
<label for="C1_type">Type 1</label>
(There are other inputs)
Are “passed back” to the View(?) form(?) via the {% render_field
{% render_field form.q_text rows="3" class="form-control" %}
{% render_field form.Choice1_text class="form-control" %}
{% render_field form.C1_Type class="form-control" %}
There are other {% render_field
The
form.q_text
form.Choice1_text
form.C1_Type
are the names of the fields in the table/model to be updated by the data typed in the boxes on the webpage.
in forms.py, there is:
from django import forms
… deleted lines …
class AnyForm(forms.ModelForm):
class Meta:
model = CC_Questions
fields = ['q_text', 'Choice1_text', 'Choice2_text','C1_Type','C2_Type']
where:
class AnyForm(forms.ModelForm):
indicates this is a “modelForm” which is “used unmodified” in a view(?) template(?) will result in a collection of boxes to accept input being displayed on the screen, without any formatting.
the:
model = CC_Questions
is the name of the model/table to which data will be written
the:
fields = ['q_text', 'Choice1_text', 'Choice2_text','C1_Type','C2_Type']
Are the fields that will display input boxes in the template and will be the fields where the data is written in the model when data is input in the webpage. This form may not display all of the fields in the model if they do not all require a value from this form.
The view is:
def p2create(request):
if request.method == 'POST':
form = AnyForm(request.POST)
if form.is_valid():
form.save()
return redirect('/polls/p2')
else:
form = AnyForm()
context = {'form' : form}
return render(request, 'pollapp2/create.html', context)
Since the template contained the line
<form method="POST">
the code below
if request.method == 'POST':
will evaluate as True resulting in the form, AnyForm, being "used"
form = AnyForm(request.POST)
if the form is valid (how can a form be “invalid??)
if form.is_valid():
then save the values that the template “passes back” to the form (??) via the
{% render_field form.<field name>
The form “passes back” values to the view (??) via the
model =<model name>
fields = [“<field1>”, “<field2>”, ….]
and the view writes to the model (??) via the
form.save()
The next web page to be displayed is
return redirect('/polls/p2')
So that is my best understanding of how working code writes data to a table.
Now follows is the code I have working to display a slider, and the slider value, when I press the “Try it” button. The code successfully displays the value corresponding to the position of the slider on the underlying range. However, from the following I can’t work out what code I need to modify to “trap” the value of “myint” and write it to the appropriate field in a new record in the model.
Template that displays the slider:
<!-- p2slider.html -->
{% extends "pollapp2/base.html" %}
<!-- load widget tools to give me more control over layout of form in template -->
{% load widget_tweaks %}
{% block title %}P2Slider test{% endblock %}
{% block main %}
<div class="row">
<div class="col-lg-8 col-lg-offset-2">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">P2Slider test</h3>
</div>
<div class="panel-body">
{% render_field form.RangeInput class="SliderForm" %}
{{form.myint}}
<label for="myint">Input Value</label>
<!-- https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_range_value2 -->
<p>Click the button to display the value of the value attribute of the slider control.</p>
<button onclick="myFunction()">Try it</button>
<p id="slider_val"></p>
<script>
function myFunction() {
var x = document.getElementById("id_myint").value;
document.getElementById("slider_val").innerHTML = x;
}
</script>
<!-- end -->
</div>
<div class="panel-footer">
Data here
</div>
</div>
</div>
</div>
{% endblock %}
Form that “relates” to this template. I notice that this code doesn’t use ModelForm
# attempt at range input slider
#
from polls.widgets import RangeInput
#
class SliderForm(forms.Form):
myint = forms.IntegerField(widget=RangeInput)
View I have copied and modified from the view for “Create” but I guess is wrong because I don’t have a model form:
def p2slider(request):
if request.method == 'POST':
form = SliderForm(request.POST)
if form.is_valid():
form.save() # ??????
context = {'form' : form} # ????
return render(request,'pollapp2/p2slider.html',context)
else:
form = SliderForm()
context = {'form' : form} # ??????
return render(request,'pollapp2/p2slider.html', context)
So I’m guessing I need to change the view function p2slider to the relevant model, cc_resp_nofk. But I don’t know what to code.
This is a simple Form, so there is no .save() method (since it is unclear what that would mean). You can obtain the value with the form.cleaned_data:
def p2slider(request):
if request.method == 'POST':
form = SliderForm(request.POST)
if form.is_valid():
slider_value = form.cleaned_data['myint']
# do something …
else:
form = SliderForm()
context = {'form' : form}
return render(request,'pollapp2/p2slider.html', context)
or if you want to use it in a ModelForm, you can plug this in as the widget of a form field:
class SomeModelForm(forms.ModelForm):
class Meta:
model = SomeModel
fields = ['intfield']
widgets = {
'intfield': RangeInput
}
If SomeModel thus has an intfield as field, you can use the RangeInput as widget for that field, and then .save() that form.
I have this edit or update form in which I want to display only the image name in the form for a better user experience so that the user could know which image he has uploaded while creating the data.
I am storing the image name in the model as well,but i want to display the image name inside the image field.
forms.py
class MenuCategoryForm(forms.ModelForm):
image = forms.ImageField(allow_empty_file=True, required=False)
class Meta:
model = MenuCategory
fields = ['name', 'description', 'menu_options']
view
def menu_category_update(request, id):
item = MenuCategory.objects.get(id=id)
if request.method == 'POST':
form = MenuCategoryForm(request.POST, request.FILES, instance=item)
if form.is_valid():
if request.FILES['image']:
image = request.FILES['image']
image_url = upload_image(image, 'menu_category', image.name)
obj = form.save(commit=False)
obj.image_url = image_url
form.save()
else:
form.save()
return redirect('menu-category')
else:
form = MenuCategoryForm(instance=item)
context = {
'form': form,
'item': item
}
return render(request, 'menu/menu_category_update.html', context)
Template
{% extends 'partials/base.html' %} {% load crispy_forms_filters %} {% load
crispy_forms_tags %}
<!-- {% block title %}Menu category {% endblock %} -->
{% block content %}
<div class="container edit-form-flex">
<div class="row Edit-form-box">
<div class="form-inner-box bg-white">
<div class="heading-editing">
<h3>Edit menu category</h3>
</div>
<form method="POST" class="add-new-form edit-form" enctype="multipart/form-data">
{% csrf_token %} {{ form|crispy }}
<div class="update-buttons-container">
<button class="btn btn-info1" type="submit" value="Update">
Update
</button>
<a class="btn btn-secondary" href="{% url 'menu-category' %}"
>Cancel</a
>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
Well in other to get the name of the image in your form you will be better off in using a form initial like so:
def menu_category_update(request,id):
item = MenuCategory.objects.get(id=id)
if request.method == 'POST':
form = MenuCategoryForm(request.POST, request.FILES, instance=item)
if form.is_valid:
obj = form.save(commit=False)
# obj.username = request.user
form.save()
return redirect('menu-category')
else:
form = MenuCategoryForm(
initial={'image':item.image,
'name':item.name,
'description':iteem.description,
})# this are the fields that we want to show in the form
context = {
'form': form,
'item': item
}
return render(request, 'menu/menu_category_update.html', context)
In your form html you will apply the form initial to the field that you want to show in your form, by doing something like this: form.initial.name As i have illustrated below :
{% extends 'partials/base.html' %} {% load crispy_forms_filters %} {% load
crispy_forms_tags %}
<!-- {% block title %}Menu category {% endblock %} -->
{% block content %}
<div class="container edit-form-flex">
<div class="row Edit-form-box">
<div class="form-inner-box bg-white">
<div class="heading-editing">
<h3>Edit menu category</h3>
</div>
<form method="POST" class="add-new-form edit-form" enctype="multipart/form-data">
{% csrf_token %}
<label> Image name </label>
{{form.initial.name}}
<label> Description </label>
{{ form.initial.description }}
<label> Image </label>
{{ form.initial.image }}
<div class="update-buttons-container">
<button class="btn btn-info1" type="submit" value="Update">
Update
</button>
<a class="btn btn-secondary" href="{% url 'menu-category' %}"
>Cancel</a
>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
i am practicing django by making an ecommerce app. I also share the video so that you can also check the problem.
https://youtu.be/crYlZ7Bo8y4
Application is working perfectly but when i filter the product according to selected category and press add to cart button then it will show all products of all categories instead of showing selected products under that category.
Can you please help me out in this.
index.html page:
{% extends 'base.html' %}
{% block content %}
{% load cart %}
{% load custom_filter %}
<!-- body -->
<div class="container-fluid mt-3">
<div class="row">
<!-- filter -->
<div class="col-lg-3 mx-auto">
<div class="list-group">
All Products
{% for category in categories %}
<a href="/?category={{category.id}}"
class="list-group-item list-group-item-action">{{category.name}}</a>
{% endfor %}
</div>
</div>
<!-- all products -->
<div id='products' class="col-lg-9 mx-auto">
<div class="row mx-auto">
{% for product in products %}
<div class="card mx-auto mb-3" id={{product.id}} style="width: 18rem;">
<img class="card-img-top" src="{{product.image.url}}" alt="Card image cap">
<div class="card-body">
<p class="card-title">{{product.name}}</p>
<p class="card-text"><b>{{product.price|currency}}</b></p>
<!-- {{product | is_in_cart:request.session.cart }} -->
</div>
<div class="card-footer p-0 no-gutters">
{% if product|is_in_cart:request.session.cart %}
<div class="row no-gutters">
<form action="/#{{product.id}}" class="col-2 " method="post">
{% csrf_token %}
<input hidden type="text" name='product' value='{{product.id}}'>
<input hidden type="text" name='remove' value='True'>
<input type="submit" value=" - " class="btn btn-block btn-light border-right">
</form>
<div class="text-center col">{{product|cart_quantity:request.session.cart}} in Cart</div>
<form action="/#{{product.id}}" class="col-2 " method="post">
{% csrf_token %}
<input hidden type="text" name='product' value='{{product.id}}'>
<input type="submit" value=" + " class="btn btn-block btn-light border-left">
</form>
</div>
{% else %}
<form action="/#{{product.id}}" method="POST" class="btn-block">
{% csrf_token %}
<input hidden type="text" name='product' value='{{product.id}}'>
<input type="submit" class="float-right btn btn-light form-control"
value="Add To Cart">
</form>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
{% endblock %}
index.py:
from django.shortcuts import render , redirect , HttpResponseRedirect
from user.models.product import Product
from user.models.category import Category
from django.views import View
# Create your views here.
class Index(View):
def post(self , request):
product = request.POST.get('product')
remove = request.POST.get('remove')
cart = request.session.get('cart')
if cart:
quantity = cart.get(product)
if quantity:
if remove:
if quantity<=1:
cart.pop(product)
else:
cart[product] = quantity-1
else:
cart[product] = quantity+1
else:
cart[product] = 1
else:
cart = {}
cart[product] = 1
request.session['cart'] = cart
print('cart', request.session['cart'])
return redirect('user:homepage')
def get(self , request):
return HttpResponseRedirect(f'/store{request.get_full_path()[1:]}')
def store(request):
cart = request.session.get('cart')
if not cart:
request.session['cart'] = {}
products = None
categories = Category.get_all_categories()
categoryID = request.GET.get('category')
if categoryID:
products = Product.get_all_products_by_categoryid(categoryID)
else:
products = Product.get_all_products();
data = {}
data['products'] = products
data['categories'] = categories
print('you are : ', request.session.get('email'))
return render(request, 'index.html', data)
urls.py:
from django.urls import path
from .views.index import Index,store
from .views.signup import Signup
from .views.login import Login,logout
app_name = 'user'
urlpatterns = [
path('', Index.as_view(), name='homepage'),
path('store', store, name='store'),
path('signup', Signup.as_view(), name='signup'),
path('login', Login.as_view(), name='login'),
path('logout', logout, name='logout'),
]
cart.py template tag:
from django import template
register = template.Library()
#register.filter(name='is_in_cart')
def is_in_cart(product , cart):
keys = cart.keys()
for id in keys:
if int(id) == product.id:
return True
return False;
#register.filter(name='cart_quantity')
def cart_quantity(product , cart):
keys = cart.keys()
for id in keys:
if int(id) == product.id:
return cart.get(id)
return 0;
#register.filter(name='price_total')
def price_total(product , cart):
return product.price * cart_quantity(product , cart)
#register.filter(name='total_cart_price')
def total_cart_price(products , cart):
sum = 0 ;
for p in products:
sum += price_total(p , cart)
return sum
Custom_filter.py template tag:
from django import template
register = template.Library()
#register.filter(name='currency')
def currency(number):
return "₹ "+str(number)
#register.filter(name='multiply')
def multiply(number , number1):
return number * number1
referer = request.META['HTTP_REFERER']
return redirect(referer)
You can get the referring page of request instead of redirecting a constant link.You can also check META
I am using WTForms in conjunction with Flask and I would like to integrate the Bootstrap Form Validation for errors in my form. I have a basic login form setup something like this:
class LoginForm(FlaskForm):
"""Login form."""
email = EmailField(
"Email Address", validators=[DataRequired(), Email(), Length(min=6, max=40)]
)
password = PasswordField(
"Password", validators=[DataRequired()]
)
def __init__(self, *args, **kwargs):
"""Create instance."""
super(LoginForm, self).__init__(*args, **kwargs)
self.user = None
def validate(self):
"""Validate the form."""
initial_validation = super(LoginForm, self).validate()
if not initial_validation:
return False
self.user = User.query.filter_by(email=self.email.data).first()
if not self.user:
self.email.errors.append("Unknown email address!")
return False
if not self.user.check_password(self.password.data):
self.password.errors.append("Invalid password!")
return False
if not self.user.verified:
self.email.errors.append("Please verify your email address!")
return False
return True
My login.html template is setup like this:
<form method="POST" action="{{ url_for('public.login') }}" role="form">
{{ form.csrf_token }}
<div class="form-group">
{{ form.email.label(class_="form-control-label") }}
<div class="input-group input-group-merge">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-user"></i></span>
</div>
{{ form.email(placeholder="name#example.com", class_="form-control") }}
</div>
</div>
<div class="form-group mb-4">
<div class="d-flex align-items-center justify-content-between">
<div>
{{ form.password.label(class_="form-control-label") }}
</div>
<div class="mb-2">
Lost password?
</div>
</div>
<div class="input-group input-group-merge">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-key"></i></span>
</div>
{{ form.password(placeholder="Password", class_="form-control") }}
<div class="input-group-append" onclick="togglePassword()">
<span class="input-group-text">
<i class="fas fa-eye"></i>
</span>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-md-auto mt-1 mb-2" align="center">
<button type="submit" class="btn btn-sm btn-primary btn-icon rounded-pill">
<span class="btn-inner--text">Sign in</span>
<span class="btn-inner--icon"><i class="fas fa-long-arrow-alt-right"></i></span>
</button>
</div>
<div class="col-md-auto text-center mt-2">
<p class="text-secondary-dark">or</p>
</div>
<div class="col-md-auto" align="center">
<button type="button" class="btn btn-md btn-secondary btn-icon-only">
<span class="btn-inner--icon">
<i class="fab fa-google"></i>
</span>
</button>
<button type="button" class="btn btn-md btn-secondary btn-icon-only">
<span class="btn-inner--icon">
<i class="fab fa-linkedin"></i>
</span>
</button>
</div>
</div>
</form>
I would like to display the errors that I validate using WTForms, but I am unsure of how to change the class of the original form element to is-invalid or is-valid, and how to create the labels for each error. I have looked into macros, but they don't seem to be able to modify the form element either.
Can someone point me in the right direction?
I faced the same problem and I wanted to avoid the use of any third package (flask-bootstrap), so I came up with this simple solution:
<div class="form-group">
{{ form.email.label }} <span class="text-danger">*</span>
{# her #}
{{ form.email(class="form-control" + (" is-invalid" if form.email.errors else "") + " rounded-0 shadow-none", **{"placeholder": "Your Email", "aria-describedby": "emailHelp", "autocomplete": "off"}) }}
<small id="emailHelp" class="form-text text-muted">We'll never share your data with anyone else.</small>
{% if form.email.errors %}
{% for error in form.email.errors %}
<div class="invalid-feedback">{{ error }}</div>
{% endfor %}
{% endif %}
</div>
The trick is to use a simple ternary expression combined with string concatenation.
#cizario's answer is a great start. However, I found a better implementation for the errors. Using WTForm's Custom Widgets, you get the following widgets:
from wtforms.widgets import PasswordInput, CheckboxInput, TextInput
from wtforms.widgets.html5 import EmailInput
class BootstrapVerifyEmail(EmailInput):
"""Bootstrap Validator for email"""
def __init__(self, error_class=u"is-invalid"):
super(BootstrapVerifyEmail, self).__init__()
self.error_class = error_class
def __call__(self, field, **kwargs):
if field.errors:
c = kwargs.pop("class", "") or kwargs.pop("class_", "")
kwargs["class"] = u"%s %s" % (self.error_class, c)
return super(BootstrapVerifyEmail, self).__call__(field, **kwargs)
class BootstrapVerifyPassword(PasswordInput):
"""Bootstrap Validator for password"""
def __init__(self, error_class=u"is-invalid"):
super(BootstrapVerifyPassword, self).__init__()
self.error_class = error_class
def __call__(self, field, **kwargs):
if field.errors:
c = kwargs.pop("class", "") or kwargs.pop("class_", "")
kwargs["class"] = u"%s %s" % (self.error_class, c)
return super(BootstrapVerifyPassword, self).__call__(field, **kwargs)
class BootstrapVerifyBoolean(CheckboxInput):
"""Bootstrap Validator for boolean"""
def __init__(self, error_class=u"is-invalid"):
super(BootstrapVerifyBoolean, self).__init__()
self.error_class = error_class
def __call__(self, field, **kwargs):
if field.errors:
c = kwargs.pop("class", "") or kwargs.pop("class_", "")
kwargs["class"] = u"%s %s" % (self.error_class, c)
return super(BootstrapVerifyBoolean, self).__call__(field, **kwargs)
class BootstrapVerifyText(TextInput):
"""Bootstrap Validator for text"""
def __init__(self, error_class=u"is-invalid"):
super(BootstrapVerifyText, self).__init__()
self.error_class = error_class
def __call__(self, field, **kwargs):
if field.errors:
c = kwargs.pop("class", "") or kwargs.pop("class_", "")
kwargs["class"] = u"%s %s" % (self.error_class, c)
return super(BootstrapVerifyText, self).__call__(field, **kwargs)
This will add the invalid tag so that bootstrap can mark it as invalid. In the HTML, do something like this to add the error message:
{{ login_user_form.email.label(class_="form-control-label") }}
<div class="input-group input-group-merge">
<div class="input-group-prepend">
<span class="input-group-text" id="user"><i class="fas fa-user"></i></span>
</div>
{{ login_user_form.email(placeholder="name#example.com", class_="form-control", **{"aria-describedby": "inputGroupPrepend3", "required": ""}) }}
{% for error in login_user_form.email.errors %}
<div class="invalid-feedback">{{ error }}</div>
{% endfor %}
</div>
I ended up using a different library that provides Bootstrap widgets:
https://github.com/agdsn/wtforms-widgets
The usage gets really simple really fast:
from wtforms import validators
from wtforms.validators import Email
from wtforms_widgets.base_form import BaseForm
from wtforms_widgets.fields.core import StringField, PasswordField
class RegisterForm(BaseForm):
email = StringField('Email Address', [Email(), validators.DataRequired(message='Forgot your email address?')])
password = PasswordField('Password', [validators.DataRequired(message='Must provide a password. ;-)')])
<form method="POST" action="{{ url_for('auth.register') }}" accept-charset="UTF-8" role="form">
{% for field in form %}
{{ field(render_mode='horizontal', autocomplete='off') }}
{% endfor %}
<input type="submit" value="submit">
</form>
But this doesn't include validation.
To add validation, I put this in Jinja:
<form method="POST" action="{{ url_for('auth.register') }}" accept-charset="UTF-8" role="form">
{% for field in form %}
{{ field() }}
{% for error in field.errors %}
<div class="invalid-feedback">{{ error }}</div>
{% endfor %}
{% endfor %}
<input type="submit" value="submit">
</form>
And for flashed messages I use this in my base layout.html that I extend everywhere:
<div>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
{% if category == 'message' %}
<div class="alert alert-warning" role="alert">
{% else %}
<div class="alert alert-{{ category }}" role="alert">
{% endif %}
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
</div>
When rendering your template using render_template, pass the 'is-valid' class to all the elements you want. Use this
class = '{{email_valid_class}} all other classes here'
Then in python return render_template('login.html',email_valid_class='is-valid')
I've got a site where I display specific values of a stock in a table and then should be able to change those values again when clicking on a button, that opens a modal with a form inside.
Now this is my first flask app and I’ve got a problem in overwriting my “original” data with the new Data I’m getting from my form. I don't really know how I can do that. My code looks like this:
views.py
#app.route("/stock-editor", methods=['GET', 'POST'])
def stock_editor():
index = Index(initial_date=app.config['INCEPTION_DATE'], initial_value=10000)
end_date = datetime.today()
start_date = app.config['INCEPTION_DATE']
empty_stocks = dict()
for symbol, shares in index.share_distribution.items(): # Here I get every empty stock and put inside a dict so that I can show it on my page
stock_value = StockValue.query.filter(StockValue.value_eur == 0.000000, StockValue.identifier == symbol, StockValue.date <= end_date, StockValue.date >= start_date).all()
if stock_value:
empty_stocks[symbol] = stock_value
if request.method == 'POST': # this is where I am failing, more info below
result = request.form
new_stock_value = request.values.get('new-stock-value')
new_source_value = request.values.get('new-source-value')
return render_template('stock-editor.html', result=result, empty_stocks=empty_stocks, start_date=start_date, end_date=end_date, new_stock_value=new_stock_value, new_source_value=new_source_value)
else:
return render_template('stock-editor.html', empty_stocks=empty_stocks, start_date=start_date, end_date=end_date)
The html:
<table class="table">
<tbody>
{% for symbol, stock_values in empty_stocks.items() %}
<tr class="table-active">
<th>Aktie</th>
<th>Datum</th>
<th>Wert</th>
<th colspan="2">Quelle</th>
</tr>
{% for value in stock_values %}
<tr>
<th>{{ symbol }}</th>
<td>{{ value.date.strftime('%d.%m.%Y')}}</td>
<td>{{ '%d' % value.value_eur }}</td>
<td>{{ value.source }}</td>
<td>
<button type="button" class="btn btn-success" data-toggle="modal" data-target="#modal-{{ value.id }}">Bearbeiten <i class="fas fa-pencil-alt"></i></button>
<!-- The Modal -->
<div class="modal fade" id="modal-{{ value.id }}">
<div class="modal-dialog">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header">
<h4 class="modal-title">Aktie bearbeiten</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<!-- Modal body -->
<div class="modal-body">
<form method="POST">
<label for="new-stock-value" class="col-form-label">Neuer Wert:</label>
<input type="number" name="new-stock-value" class="form-control" id="new-stock-value" placeholder="{{ '%d' % value.value_eur }}">
<label for="new-source-value" class="col-form-label">Quelle:</label>
<input type="text" name="new-source-value" class="form-control" id="new-source-value" placeholder="{{ value.source }}">
<!-- Modal footer -->
<div class="modal-footer">
<button type="submit" class="btn btn-success">Ändern</button>
<button type="button" class="btn btn-danger" data-dismiss="modal">Abbrechen</button>
</div>
</form>
</div>
</div>
</div>
</div>
</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
So in the if request.method == 'POST' I am getting my new values (new_stock_value and new_source_value) but I’m a little lost in HOW to actually set them as the new value of the stock where I am setting those new values?
Add a hidden field like below to the modal form,
<input type='hidden' name='id' value='{{ value.id }}'>
The above code snippet allows you to send the id of the current stock which is being edited. And in the route you can grab this id by,
id = request.values.get('id')
Using this id, update the empty_stocks dictionary with the new value of stock.
for symbol, stock_values in empty_stocks:
for value in stock_values:
if value.id == id:
value.value_eur = request.values.get('new-stock-value')
value.source = request.values.get('new-source-value')
break
I hope this helped you.