How to style WTForm field through a widget? - widget

I want to style a wtform field through a custom widget but the rendering keeps returning raw html code (with ampersands). Why?
this is my widget:
def my_widget(field, **kwargs):
field_id = kwargs.pop('id', field.id)
html = [u'<input class="my_class" %s value="" type="text">' % html_params(id=field_id, name=field_id)]
return u''.join(html)

You need to tell than string should be rendered as is without escaping HTML:
from wtforms.widget.core import HTMLString
def my_widget(field, **kwargs):
....
return HTMLString(u''.join(html))

Related

editing tag.string property with BeautifulSoup (bs4) to string including markup

I have an html document that I wish to edit such that any word(s) within it can be highlighted/made bold.
I have the html in memory and have passed to BeautifulSoup. I iteratate through all tags and take their string elements. If any string contains a matching word, I edit the string and replace it into the html with markup wrapping around the desired word.
from flask import Flask, Markup
from bs4 import BeautifulSoup
def match( documentText: str, searchQuery: str) -> Markup:
words = documentText.split( ' ')
if len( words) >= 3:
words[2] = f'<strong>{ words[2]}</strong>'
logger.info( f'{ words=}')
return Markup( ' '.join( words))
for link in html.find_all( True):
if ( link.string):
link.string = match( link.string, searchQuery)
app = Flask( __name__)
#app.route( '/')
def home():
logger.info( 'trying markup and testing logging')
return str( html), 200
app.run( debug=True)
Now, instead of rendering a page with bold words where I would like them, I visually see the html tags, this is because if I view source, the tags are actually represented by >. This would appear to be coming from the line "link.string = match( link.string, searchQuery)" - which I guess could well make sense, in that BeautifulSoup is doing type checking and ensuring the only thing that goes in the tag.string field is indeed a string. The ideal end state I guess then would be to make a branch off the tag to include a child tag.
Is this a problem anybody else has previously solved? My solution to this whole thing seems chunky and inelegant so I wouldn't mind a better route if anybody has one.
For a quick fix, just replace back those html special characters with str.replace()
from flask import Flask, Markup
from bs4 import BeautifulSoup
# ...
#app.route( '/')
def home():
logger.info( 'trying markup and testing logging')
return str(html).replace(">",">").replace("&lt","<"), 200
app.run( debug=True)
Be careful since html special characters is not just < and >
Html special characters reference: https://www.html.am/reference/html-special-characters.cfm
Better Approach:
This approach will change all html special chars back to it's un-escapced form
import html.parser
htmlparser = html.parser.HTMLParser()
html_decoded_string = parser.unescape(str(html))
return html_decoded_string , 200
do note that on Python 2, the import statement (module name) might be a little bit different

How can I use pagination when I am using a form to input a date and submitting that form sends a post request and pagination accepts get request?

I am using django to display a webpage. I want to display details of files from database. These files can be of any number. I am using a form to input a date from HTML file which is accepted by a django view function. This function checks if the request is of type POST and returns the data between the mentioned date. Now when I use pagination to display these pages, pressing the next doesn't show anything as this time the request gets changed to GET. In my django view function, data is fetched in a list. Every file's data is in a list. So, all the data consists lists of lists. How will I be able to display these lists using pagination without sending the requests again. Is it possible to do so?
My data is stored in a database according to dates. I am fetching the data in a given range. This range comes from html page. Here is how I am trying to fetch the details:
<div class="container">
<form method="POST">
{% csrf_token %}
<input type="date" name="start_date">
<input type="date" name="end_date">
<button type="Submit">Get Details</button>
</form>
</div>
This is the code that I use at the backend to serve the webpage.
def data_range(request):
if request.method == 'POST':
form = Form(request.POST)
try:
if form.is_valid():
start_date = form.data.get('start_date')
end_date = form.data.get('end_date')
page_data = get_page_details(start_date, end_date)
else:
current_date = datetime.datetime.now()
start_date = end_date = current_date.strftime("%Y-%m-%d")
page_data = get_page_details(start_date, end_date)
except Exception:
return render(request, 'UserInterface/no_data_fort.html', {'list_item_count': 0})
else:
return render(request, 'UserInterface/no_data_fort.html', {'list_item_count': 0})
if page_data:
# Calculating total files in fortnightly data
list_item_count = len(page_data)
# Adding Paginator or pagination
paginator = Paginator(page_data, 1)
page = request.GET.get('page')
data = paginator.get_page(page)
return render(request, 'UserInterface/fortnightly_range.html', {'page_data_list': data, 'list_item_count': list_item_count})
else:
return render(request, 'UserInterface/no_data_fort.html', {'list_item_count': 0})
page_data that I fetch is a list of lists.

List(array) of HTML element with django 2.0.6

I'm passing a list of HTML element to the views.py from html through post but Im just getting the last value.
here is the html code that i used, multiple lines of this one
<input name="idborrow[]" id="borrow" value='+element[i].id+'>
and here is my code in the views.py
if request.method == 'POST':
idborrow = request.POST.get('idborrow[]', '')
print (idborrow)
in the console, it just prints the last value, how to get the whole list of values
Try using getlist
Ex:
request.POST.getlist('idborrow[]')

Wtforms.fields.html5 DateField not working as plain old DateField in Flask

I have a problem getting the HTML5 Datepicker to provide a value to a form in Flask using WTF-Forms. The same form is working as expected if I drop the HTML5 DateField to a plain old vanilla WTF-Forms DateField.
For the curious: Versions: Python 3.5.2, Flask: 0.11.1, Flask-WTF: 0.12
The relevant code would be:
Model:
class Order(db.Model):
__tablename__ = 'orders'
paid_date = db.Column(db.DateTime, nullable=True)
Form shouldn't make a difference but for completeness here is the snippet from the order-update.html:
<div class="form-group">
{{ form.paid_date.label }}
{% if form.paid_date.errors %}
{% for error in form.paid_date.errors %}
<p class="error-message">{{ error }}</p>
{% endfor %}
{% endif %}
{{ form.paid_date }}
</div>
Minimal Controller:
#app.route('/orders/update/<int:order_number>', methods=['GET', 'POST'])
def update_order(order_number):
order = Order.query.get(order_number)
if request.method == 'POST':
if not form.validate():
return render_template('update-order.html', form=form, order=order)
else:
form.populate_obj(order)
db.session.commit()
return redirect(url_for('user')
elif request.method == 'GET':
return render_template('update-order.html', form=form, order=order)
Here is what works for the form declaration:
from flask_wtf import Form
from wtforms import DateField
class UpdateOrderForm(Form):
paid_date = DateField('Date Order Paid', format='%m/%d/%y',
render_kw={'placeholder': '6/20/15 for June 20, 2015'})
submit = SubmitField('Update Order')
while the following will break:
from flask_wtf import Form
from wtforms.fields.html5 import DateField
class UpdateOrderForm(Form):
paid_date = DateField('Date Order Paid', format='%m/%d/%y',
render_kw={'placeholder': '6/20/15 for June 20, 2015'})
submit = SubmitField('Update Order')
To describe the behavior, with the plain DateField, I render a form with a string input (which also is displaying the present value as it should after the form.populate_obj() that allows me to stick in a new date, e.g., 9/12/16, and it passes back to the controller with form.paid_date is a DateField, has a value of 09/20/16, and form.paid_date.data=2016-09-20. This passes validation and commits to the db.
When I switch to the HTML5 date picker, which would be very nice to have, I render a form with a datepicker (I would like to set this to the present date in the object btw) that has form.paid_date as above, i.e., DateField and with the value the same. However, the datepicker is showing mm/dd/yyyy. So if I enter 09/20/2016 either by hand or with the picker and submit, form.paid_date.data comes back None. Furthermore, I have
process_errors = <class 'list'>: ['Not a valid date value']
raw_data = <class 'list'>: ['2016-09-20'].
So I see the data is returned, but doesn't pass the form processing and thus get placed into the data slot. I can write code to pull it out from raw_data but surely I am missing something or this is a bug!?
I found out that in Chrome browser raw_data is ['2011-01-01'] so it doesn't match the time format!
you should change it like this:
format='%Y-%m-%d',

Django Template render dictionary values into html form using Function Based View

Summary
Using Django 1.8, I'm trying to make a Function Based View that renders an html page that allows me to update the contents of the object. I'm able to get this to work by using the form.as_p as shown in the documentation here, but I can't get these values inside an html <input> as the value.
Issue
The issue is that only the first word appears and the rest of the text is cut off (e.g. for a html input tag, the value of 'Hello World Will' gets turned into 'Hello')
model.py
class Questions(model.Model):
title = models.CharField(max_length=512, null=False, blank=False)
forms.py
class QuestionsForm(forms.ModelForm):
class Meta:
model = Questions
fields = ('title', )
views.py
def advice_update(request, pk)
question_results = Questions.object.get(id=pk)
advice_form = QuestionsForm(request.POST or None, instance=question_results)
...
return render(request, 'advice/advice_update.html', {'advice_form': advice_form, 'question_results': question_results,})
advice_update.html
<form method='POST' action=''>{% csrf_token %}
# METHOD 1 - This code works and renders the form with paragraphs enclosed
# but I want more control
{{ advice_form.as_p }}
# METHOD 2 - When I try to get the value by itself, it works too
{{ advice_form.instance.title }} # E.g. 'Hello World'
{{ question_results.title }} # E.g. 'Hello World'
# METHOD 3 - When I try to put the text inside a 'value' tag in an 'input',
# the text gets cut off and only the first word appears in the input
# When I look at the console, I see the rest of the text in there.
<input id="id_title" type="text" name="title" class="form-control" value={{ question_results.title }}>
I tried a few things like adding autoescape and safe tags, but when I use METHOD 3, the value inside the tag of advice.html cuts off when there's a space (e.g. 'Hello World' turns into 'Hello').
First, you don't need to set null=False, blank=False on your title field as those are there by default.
It looks like the main issue you're having is adding the Bootstrap css class to the form element, which you can accomplish a couple of different ways. Also note that your value attribute is missing quotes around it.
The first way is to add the appropriate classing to the widget from the Python side of the form:
class QuestionsForm(forms.ModelForm):
class Meta:
model = Questions
fields = ('title', )
def __init__(self, *args, **kwargs):
super(QuestionsForm, self).__init__(*args, **kwargs)
self.fields['title'].widget.attrs['class'] = 'form-control'
However, this gets really tedious when you're dealing with a lot of form fields. So, I use django-widget-tweaks to instead add these at the template level:
{{ form.title|add_class:"form-control" }}
which I find a lot easier to deal with. This way you don't have to render the field by hand. Also be sure to handle the case if there's no matching question in your view:
question = get_object_or_404(Question, id=pk)