Flask WTForms SelectField add placeholder or disabled option - html

I am working on a webpage using flask and wtforms. When opening the webpage, my selectfield should not hold any value (i.e. be blank, have placeholder saying "please choose an option" or something in that direction).
My form is defined like this in forms.py:
class Form(FlaskForm):
selectfield = SelectField('Title', choices=[])
I leave choices as an empty list because they are created from a database through the function get_choices:
# create instance of form
form = Form()
# run function to get data from db
form.selectfield.choices = get_choices()
Here it starts to get gnarly: Since the placeholder value should be empty (i.e. "") or something like "please choose" I don't want to have it in my database. So I add the value manually:
# append
form.selectfield.choices.append('Please choose')
The html part, where I render the form looks like this:
<form method="POST" action= {{ url_for('index') }}>
{{ form.csrf_token }}
{{ form.selectfield(class_="form-control", **{"onchange":"this.form.submit()"}) }}
</form>
What have I tried:
adding 'placeholder = "please choose"' here:
{{ form.selectfield(placeholder="please choose", class_="form-control", **{"onchange":"this.form.submit()"}) }}
(as suggested by Crast here: WTForms Can I add a placeholder attribute when I init a field?)
adding default="Please choose" to my Form class as suggested by Liu Yue (How do you set a default value for a WTForms SelectField?):
class Form(FlaskForm):
selectfield = SelectField('Title', choices=[], default="Please choose")
This works partly, but the Please Choose value should not be selectable which it still is.
I feel like I might be completely on a wrong path here, and maybe oversee a very simple feature. I really can't believe that such a popular feature is not available using wtforms.
I am thankful for any advice and guidance.

Related

Bootstrap 5 Form Floating Labels are always in the upper position when using flask-wtforms

I am using flask, jinja, and wtforms to make an account creation page, and am using bootstrap's floating labels. My issue is that the labels start in the upper "floating" position on page load, as if there was something typed in to the fields, but there is nothing entered. The labels do not drop down to their default position even if I click and then click away from the elements, or type, delete it, and then click away.
Relevant sections of python:
class createAccForm(FlaskForm):
fname = StringField('First Name', [DataRequired(dataReqMsg), VCName])
lname = StringField('Last Name', [DataRequired(dataReqMsg), VCName])
email = EmailField('Email', [DataRequired(dataReqMsg), VCEmail, Email(emailMsg)])
password = StringField('Password', [DataRequired(dataReqMsg), VCPassword])
password1 = StringField('Password1', [DataRequired(dataReqMsg), VCPassword, PasswordsMatch])
submit = SubmitField('Submit')
#app.route("/createAccount")
def createAccount():
form = createAccForm()
return render_template("createAccount.html", form = form)
Relavent HTML (with jinja):
[I only included one of the form elements, all the rest are the same.]
<div class="form-floating mb-3">
{{form.fname(class="form-control", id="fname")}}
<label for="fname">First Name</label>
</div>
{% if form.errors.fname %}
{{form.errors.fname[0]}}
{% endif %}
This is what the element looks like on load:
P.S.
When I inspect the elements, I see they have the value attribute given to them by wtforms (see below), but not assigned to anything. Could that be causing bootstrap to move the labels upwards? If so, is there a way to fix it other than writing some javascript to remove the attributes (and possibly triggering a change event if necessary)?
The input element in the inspector window:
<input class="form-control" id="fname" name="fname" required type="text" value>
EDIT: I added the following code in a script tag, which did as it was supposed to (none of the inputs have that unassigned value tag anymore), but it did not fix the floating labels.
$("document").ready( () => {
$("input").removeAttr("value");
});
Apparently the placeholder attribute is required (as it says in the very first paragraph of the bootstrap docs). I should read better.
P.S.
It turns out the unassigned value attributes had nothing to do with the issue. But, if you do ever need to change what wtforms renders, this answer leading to the rendering widgets page on the wtforms docs can help.

Django Admin: How to use the button to move to the selected URL?

I am very new to Django and trying to make my first project. I find it difficult to use the buttons I created to move to the selected URL.
Let's say my app is called TestForms and my models are: Patients, General, PST and ERBT. I would like to create two buttons - 'Previous' and 'Next' - which will be used to go to previous/next forms respectively. I try to do so using admin templates in django.
NOTE: I know changing built-in templates are not a very good idea, I will create new html file to extend this templates before doing changes on the server. For now I am doing it locally on my computer.
In submit_line.html I created two new buttons and they are like so:
{% if show_save_and_go_to_next_form %}<input type="submit" value="{% translate 'Next' %}" class="default" name="_gotonextform">{% endif %}
{% if show_save_and_go_to_previous_form %}<input type="submit" value="{% translate 'Previous' %}" class="default" name="_gotopreviousform">{% endif %}
This gives me two good-looking buttons on the site.
But these are just saving the results (working like 'Save' button), but not redirecting me to the next form as I would like to. When I am adding a new patient (admin/TestForms/patient/add/), after clicking on 'Next' I would like the server to save this patient and redirect me to admin/TestForms/general/add/ to be able to fullfil the next form, then save the changes and move on to admin/TestForms/PST/add/and so on.
I know I have to add the anchor, but I tried multiple times with different approaches and nothing worked. When I try to use <a href ...>, the button disappears. Also it is difficult for me to figure out how to move from one form to another and to disable the 'Previous' button on the first form and the 'Next' button on the last form.
Any suggestions how to achieve it?
The redirect needs to be done in your view, not in the template.
def your_view(request, *args, **kwargs):
# your code ...
if request.POST.get('_gotonextform'):
return redirect('admin/TestForms/general/add/')
else:
# do whatever you like if any other button was clicked
pass

Creating a alert functionality in django wagtail

I am trying to create a alert section for a menu (similar to the menu on this page https://metageeky.github.io/mega-menu/responsive-header.html)
*Each alert should have an effective date (date alert is “posted” live) and resolved date (date alert is “removed” live). Each alert will also have a maximum of one to two sentences of text describing situation.
The number of active/current alerts will appear in parenthesis following the icon and ALERT link text.
The icon and text are Dark Orange. When you hover over the icon and text, an underline appears.
When users click on the link, they are taken to a page that lists all active alerts. At bottom of page, message displays “If you are experiencing an issue, please contact us at....”
If there are no Alerts:
The number of alerts in parenthesis following the icon and link text will not appear.
Both the icon and alert text will be Primary Blue.
When Users click on the link, they are taken to a secondary alerts page that displays a message that says “There are currently no active alerts. If you are experiencing an issue, please contact us at...”
How would i achieve this?
Thank you.
There is a lot to unpack in your question but here is a high level approach.
1. Define your model
Read the Django docs on how to create a Model
Read the Django docs on what types of Fields exist
In your models.py, you will need to create a new model that has all the data you need for your requirements.
from django.db import models
class Alert(models.Model):
title = models.CharField()
description = models.TextField()
date_from = models.DateTimeField()
date_to = models.DateTimeField()
2. Ensure you can edit/manage your model data
Now you need to provide a way for your admin users to access the data model, edit & create items.
Wagtail has a great Snippets feature that allows this to work without too many changes, you will need to add #register_snippet on your model and also define some panels.
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.snippets.models import register_snippet
from django.db import models
#register_snippet
class Alert(models.Model):
#... fields (defined above)
panels = [
FieldPanel('title'),
FieldPanel('description'),
FieldPanel('date_from'),
FieldPanel('date_to'),
]
def __str__(self):
return self.title
3. Prepare a template tag to show the queried data
Now you will need to work out how to query the model in a way that it will return the alerts based on your requirements (current date should be within the date range of the data).
Django has docs on writing queries
The simplest way to get the results of this query into the template will be with a custom Template Tag
An inclusion_tag is a way to have a small template fragment that can be used anywhere with custom data (without having to pass it into each View).
In the example below, you will still need to create the template file current_alerts.html which will contain how you want to render the alerts.
In your template tag template you can also use the page_url tag to provide a link to the alerts_page
# template_tags/custom_tags.py
# remember to create a template_tags/__init__.py file also
from django import template
from .models import Alert
register = template.Library()
#register.inclusion_tag('current_alerts.html')
def show_alerts():
# just returns all alerts, but this query can be refined to suit what you need
current_alerts = alerts.Objects.all()
alerts_page = AlertPage.Objects.all().first() # this assumes there will only ever be one
return {'alerts_page',alerts_page,'current_alerts': current_alerts}
4. Use your template tag & add styling
Now you need to include the tag at the top of the page inside your root/shared template.
{% extends "base.html" %}
{% load custom_tags %}
{% block body_class %}template-blogpage{% endblock %}
​{% show_alerts %}
{% block content %}...{% endblock %}
5. Create a AlertsPage
You will need to create a new Page type to redirect users to within your alerts link.
https://docs.wagtail.io/en/stable/topics/pages.html
This Page can be anywhere in your tree and the Page's view template can also use the same shared template or you can pass the alerts to the view via the template context

How can I set choice values in twig

When I put this code I have an error.
Impossible to access an attribute ("value") on a string variable ("Etudiant").
CODE:
{{ form_widget(registrationForm.typePerso,
{'choices': {'Etudiant': 'Etudiant','Enseignant' : 'Enseignant'}})
}}
This is from symfony Github:
"you cannot, because choices impact the building of the form. They are not passed as is to the template (they main goal of the Form component is not to handle the rendering, but to handle the form binding)"
https://github.com/symfony/symfony/issues/18950

Django Wagtail ajax contact form

I have a contact form that appears at the bottom of every page in the footer. I simply included it in my base.html at the bottom. Now i want to figure out a way to submit it. All the examples wagtail provides is under the assumption i have an entire page dedicated to it and hence submits to itself.
This cannot work for me as its not a page.
I have written pseudo code of what I think it should look like .
def submitContact(request):
source_email = request.POST.get('email')
name = request.POST.get('name')
message = request.POST.get('message')
if (source_email is not None) and (name is not None) and (message is not None):
body = "sample"
send_mail(
name,
message,
source_email,
['test#foobar'],
fail_silently=False,
)
Then my form would be something like this
<form class="form-group" role="form" method="post" action="/submitContact">
......
</form>
Ideally if someone could point to Wagtail resources that show how to create endpoints in models that do not inherit from the Page model and are not snippets that maintain "request" content that would be useful. Ideally what I would prefer is to log this data into contact "table" then send the email after.
What should I add to my urls.py to reroute the request with the correct context for the function to retrieve the required variables and thus send the email
Additional info
I wrapped a snippet around the footer to provide some context to it using templatetags, just putting this out there incase it adds value
See below.
#register.inclusion_tag('home/menus/footer.html', takes_context=True)
def footers(context):
return {
'footers': Footers.objects.first(),
'request': context['request'],
}
You should use {% url %} template tag.
urls.py :
from django.conf.urls import url
from yourapp.views import submitContact
urlpatterns = [
url(r'^contact/$', submitContact, name='contact'),
]
Template :
<form class="form-group" role="form" method="post" action="{% url 'contact' %}">
......
</form>
Another improvement is to use Django Form.
Note : prefer lower_case_with_underscores naming style for functions. Use CamelCase for classes. See PEP8 for more information.
Instead of trying to build it yourself, why not take a look at the already existing Form Builder of Wagtail?
It enables you to create a FormPage on which you can display a custom form and even E-mail the results.
Check out the documentation here.