Create a FlaskForm dropdown based on the query string value - javascript

class DownloadForm(FlaskForm):
products = service.get_products()
choices = [(product, product.replace('-', ' '))
for product in products]
application = SelectField(
'Application',
[DataRequired()],
choices=choices
submit = SubmitField('Submit')
#home.route('/downloads/<application_name>', methods=['GET', 'POST'])
def downloads():
download_form = DownloadForm()
My route will be downloads/application_name.
Can I send this routevalue "application_name" to the form to filter out the choices in the Download Form?
I'm new to entire flask and python.

You can use a factory function to use the parameter from the URL to do your database query. Based on this query, a form is then created that can be used within the route.
def download_form_factory(name):
products = [
'Archiver',
'Editor-1',
'Editor-2',
'Music-Player',
'Video-Player'
]
class DownloadForm(FlaskForm):
app = SelectField(
'Application',
choices= [
(product, product.replace('-', ' ')) \
for product in filter(lambda p: name.lower() in p.lower(), products)
]
)
submit = SubmitField('Download')
return DownloadForm
The following example uses an input field of type QuerySelectField from WTForms-SQLAlchemy to facilitate database queries. As you can see above, the functionality is also possible with a normal SelectField.
A number of products are added to the database which, in addition to their name, have an additional property build.
If the user selects a name on the index page, all products that have the selected name are available for selection. He can then choose between the offered versions.
The form is created inside a function. The name to be searched for is passed to this function as a parameter. This can then be used to create the database query.
def download_form_factory(name):
# Create a class of the form.
class DownloadForm(FlaskForm):
# Create the select field
app = QuerySelectField(
# with a label
'Application',
# and the database entries containing the given name as options
query_factory=lambda: Product.query.filter(Product.name.ilike(f'%{name}%')).all(),
# and a label for each of these options.
get_label=lambda p: '%s (Build %s)' % (p.name, p.build),
# An option must be selected by the user.
validators=[InputRequired()]
)
# Create the Submit button.
submit = SubmitField('Download')
# Return the created form class.
return DownloadForm
Within the endpoint, the function mentioned is used and an instance of the form is created, which can be used in the usual way.
#app.route('/download/<string:name>', methods=['GET', 'POST'])
def download(name):
# Create the form and an instance of it.
form = download_form_factory(name)(request.form)
if form.validate_on_submit():
# If the POST request is sent with valid information,
# the form data can be requested.
# The result is the selected database object.
print(form.app.data)
return render_template('download.html', **locals())
Here is the complete example.
Flask (app.py)
from flask import (
Flask,
render_template,
request,
)
from flask_wtf import FlaskForm
from flask_sqlalchemy import SQLAlchemy
from wtforms import SubmitField
from wtforms_sqlalchemy.fields import QuerySelectField
from wtforms.validators import InputRequired
app = Flask(__name__)
app.secret_key = b'your secret here'
db = SQLAlchemy(app)
class Product(db.Model):
__table_args__ = (db.UniqueConstraint('name', 'build'),)
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False)
build = db.Column(db.String, nullable=False)
# Create a class of the form.
def download_form_factory(name):
class DownloadForm(FlaskForm):
app = QuerySelectField(
'Application',
get_label=lambda p: f'{p.name} (Build {p.build})',
query_factory=lambda: Product.query.filter(Product.name.ilike(f'%{name}%')).all(),
validators=[InputRequired()]
)
submit = SubmitField('Download')
return DownloadForm
# Fill the database with sample data.
with app.app_context():
db.drop_all()
db.create_all()
products = [Product(name=f'Product-{str(i).zfill(2)}', build=f'{j}') \
for i in range(1, 21) for j in range(1,i+1)]
db.session.add_all(products)
db.session.commit()
#app.route('/')
def index():
products = Product.query.group_by(Product.name).order_by(Product.name).all()
return render_template('index.html', **locals())
#app.route('/download/<string:name>', methods=['GET', 'POST'])
def download(name):
# Create the final object of the form.
form = download_form_factory(name)(request.form)
if form.validate_on_submit():
# Inquire about the selected product.
print(form.app.data)
return render_template('download.html', **locals())
HTML (templates/index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Index</title>
</head>
<body>
<ul>
{% for prod in products -%}
<li>{{prod.name}}</li>
{% endfor -%}
</ul>
</body>
</html>
HTML (templates/download.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Download</title>
</head>
<body>
<form method="post">
{{ form.csrf_token }}
<div>
{{ form.app.label }}
{{ form.app() }}
</div>
{{ form.submit() }}
</form>
</body>
</html>

Related

How to Get the data from a SelecMultipleField WTForms, in Flask SQAlchemy [duplicate]

This question already has answers here:
Get the data received in a Flask request
(23 answers)
Closed last year.
I have a question, regarding my implementation of WTForms SelectMultipleField.
Whenever the form is submited, and the database is checked, I only find the first data inputed by the user, not the entire list. How could this implementation work for the whole list of submitted results?
This is what shows in the db
ID
fav_colors
1
Blue
2
Red
And this is what should be shown per the user input
ID
fav_colors
1
Yellow,Red,Blue
2
Red, Yellow
forms.py
class ColorForm(FlaskForm):
fav_colors = SelectMultipleField(
' Select your Colors:',
choices = [
( 1, 'Blue'),
( 2, 'Red'),
( 3, 'Yellow')
])
routes.py
#route.('pick-your-color.html', methods=['GET', 'POST'])
def pick_your_color():
form = ColorForm(request.form)
form.fav_colors.data = None
if request.method == "POST":
fav_colors = request.form['fav_colors']
register_colors = Colors(fav_colors = fav_colors)
db.session.add(register_colors)
db.session.commit()
return render_template('index.html')
model.py
class Colors(db.Model):
__tablename__ = "PickedColors"
id = Column(integer, primary_key=True)
fav_colors = Column(String)
pick-your-color.html
{% extendes "layout/base.html" %}
{{ form.fav_colors.label }}
{{ form.fav_colors(class="form-control", id=fav-colors") }}
{% block javascripts %}
//multiple select field
var selectStatesInputEl = d.querySelector('#fav-colors');
if(selectStatesInputE1) {
const choices = new Choices(selectStatesInputE1);
}
{% endblock javascripts %}
P.S. I am using this JS so I can make it like the template I am using
The answer is quite simple:
fav_colors = request.form.getlist('fav_colors')
There is a similar question to this problem # Flask App Using WTForms with SelectMultipleField

Django and jQuery: jQuery not working, but receiving no errors

I cannot figure out why my jQuery script isn't working. I've read through a bunch of stackOverflows but haven't been able to fix this issue.
I have a form, and when a checkbox is checked, I want to enable a form field with the id div_id_newLockName. If the checkbox is unchecked, I want the form field to be disabled and greyed out. The checkbox has id div_id_newNameBool I have written code that I think should work, but it doesn't. I'm not receiving any errors though, which makes troubleshooting difficult. I'm hoping a second set of eyes can help. A picture of what the form looks like can be found here: In this picture, the "change name?" checkbox corresponds with div_id_newNameBool, and the "Lock Name" char field is what I want to toggle enable or disable.
base.html (contains jQuery script)
<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<style>
<!-- unimportant css stuff -->
</style>
<body class="w3-content" style="max-width:1200px">
<!-- !PAGE CONTENT! -->
{% block content %}{% endblock %}
<!-- End page content -->
</div>
<script>
$(document).ready(function(){
if ($("#div_id_newNameBool").is(':checked')){
$("#div_id_newLockName").prop("disabled", false);
}
else {
$("#div_id_newLockName").prop("disabled", true);
alert("DISABLED");
}
});
</script>
</body>
</html>
HTML page for form
{% extends "homepg/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<!-- Top header -->
<header>
</header>
<form method="POST">
<div class="w3-display-container w3-container">
{% csrf_token %}
<fieldset class="form-group" style="font-size:20px">
<legend class="border-bottom mb-4" style="font-size:20px, p">Edit Lock Information </legend>
{{ form|crispy }}
</fieldset>
<button type="submit" style="width: 200px; padding: 10px; left: 50%; margin:10px;">Change Lock</button>
</div>
</form>
{% endblock content %}
models.py
class Lock(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
lockName = models.CharField(max_length=20)
roomLoc = models.ForeignKey(Room, on_delete=models.CASCADE, null=True, blank=True)
state = models.BooleanField(default=0)
code1 = models.PositiveIntegerField(default=0, validators=[MinValueValidator(1000), MaxValueValidator(9999)])
code2 = models.PositiveIntegerField(default=0, validators=[MinValueValidator(1000), MaxValueValidator(9999)])
code3 = models.PositiveIntegerField(default=0, validators=[MinValueValidator(1000), MaxValueValidator(9999)])
code4 = models.PositiveIntegerField(default=0, validators=[MinValueValidator(1000), MaxValueValidator(9999)])
# create a dunder method to control how we want this to printed out
def __str__(self):
return self.lockName
forms.py
class changeLockForm(forms.Form):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(changeLockForm, self).__init__(*args, **kwargs)
self.fields['roomLoc'] = forms.ModelChoiceField(queryset=Models.Room.objects.filter(owner=None) | Models.Room.objects.filter(owner=user), label='Room Location')
self.fields['lockName'] = forms.ModelChoiceField(queryset=Models.Lock.objects.filter(owner=None) | Models.Lock.objects.filter(owner=user), label="Current Lock Name")
newNameBool = forms.BooleanField(label='Change Name? Y/N')
newLockName = forms.CharField(min_length=2, max_length=20, label='Lock Name', disabled=True)
#roomLoc = forms.ModelChoiceField(queryset=Models.Room.objects.filter(user=request.user), label='Room Location')
state = forms.BooleanField(required=False, label='LOCKED/UNLOCKED')
code1 = forms.IntegerField(initial=1000, min_value=1000, max_value=9999, label='First Code')
code2 = forms.IntegerField(initial=1000, min_value=1000, max_value=9999, label='Second Code')
code3 = forms.IntegerField(initial=1000, min_value=1000, max_value=9999, label='Third Code')
code4 = forms.IntegerField(initial=1000, min_value=1000, max_value=9999, label='Fourth Code')
class Meta:
model = Models.Lock
fields = ('lockName','newNameBool','newLockName','roomLoc', 'state', 'code1', 'code2', 'code3', 'code4')
field_order = ['lockName', 'newNameBool', 'newLockName', 'roomLoc', 'state', 'code1', 'code2', 'code3', 'code4']
views.py
def changeLock(request):
if request.method == "POST":
# if get post request will create form that has request.POST data!
form = changeLockForm(request.POST, user=request.user)
if form.is_valid():
locksObj = Model.Lock()
# will tell us if form valid when submitted
# form.cleaned_data is a DICTIONARY
tempBool = form.cleaned_data['newNameBool']
if tempBool:
locksObj.lockName = form.cleaned_data['newLockName']
elif not tempBool:
locksObj.lockName = form.cleaned_data['lockName']
else:
print("WHOOPSIE! clearly I coded something wrong wtf")
locksObj.state = form.cleaned_data['state']
locksObj.roomLoc = form.cleaned_data['roomLoc']
locksObj.code1 = form.cleaned_data['code1']
locksObj.code2 = form.cleaned_data['code2']
locksObj.code3 = form.cleaned_data['code3']
locksObj.code4 = form.cleaned_data['code4']
locksObj.owner = request.user
# get the old alarm name
allCodes = Model.Lock.objects.filter(owner_id=request.user)
# old Alarm Name
currentName = allCodes.values_list('lockName', flat=True)[0]
# if the old alarm name and new alarm name aren't equal...
# delete the old alarm entry
entry = Model.Lock.objects.get(lockName=str(currentName))
entry.delete()
# replace it with the new data
locksObj.save()
messages.success(request, 'Lock ' + str(locksObj.lockName) + ' edited!')
return redirect("status-page")
else:
# if not POST request, will create a blank form!
form = changeLockForm(user=request.user)
return render(request, 'users/changeLockForm.html', {'form': form})

Dynamic Loading on Flask Site with Unique URL

I am building a Flask site that dynamically loads some information like the time but I'm running into some issue with AJAX with jQuery.
Following the example on the flask site https://flask.palletsprojects.com/en/1.1.x/patterns/jquery/
I want to change it to a dynamic URL based on the users unique ID instead of a static one.
Python File:
from flask import Flask, jsonify, render_template, request
app = Flask(__name__)
#app.route('/_add_numbers/<UNIQUEID>')
def add_numbers():
a = request.args.get('a', 0, type=int)
b = request.args.get('b', 0, type=int)
return jsonify(result=a + b)
#app.route('/')
def index():
return render_template('index.html')
The HTML
<script type=text/javascript>
$(function() {
$('a#calculate').bind('click', function() {
$.getJSON($SCRIPT_ROOT + '/_add_numbers/UNIQUEID', {
a: $('input[name="a"]').val(),
b: $('input[name="b"]').val()
}, function(data) {
$("#result").text(data.result);
});
return false;
});
});
</script>
<h1>jQuery Example</h1>
<p><input type=text size=5 name=a> +
<input type=text size=5 name=b> =
<span id=result>?</span>
<p><a href=# id=calculate>calculate server side</a>
I cannot figure out how to put a unique ID into the HTML. Any help is much appreciated!
There are a variety of ways to do this. I would recommend simply adding the user's id into the template context, with render_template("index.html", user_id=current_user.id) (make sure you import current_user from flask. This will return a user id if the user is logged in, and None if not. Then you can do the following:
$(function() {
$("a#calculate").bind("click", function() {
$.getJSON($SCRIPT_ROOT + "/_add_numbers/{{ user_id }}",
etc etc: your code goes here
)}
)}
That should do it.
Alternatively you could actually use the value of current_user.id (if you feel you understand it) in the template, as this is implicitly passed down with Jinja anyway. o substitute {{ user_id }} with `{{ current_user.id }}.

Integrating typeahead.js with Django apps

I'm fairly new to Django and am trying to integrate typeahead.js to my Django app. I want to be able to search through the field 'name' in my local sql2lite DB which has around 15,000 entry. The table name is called 'institution'.
My html is able to find typeahead.js and have the following script to run it:
<script type="text/javascript" src="{% static 'js/typeahead/typeahead.jquery.js' %}">
$(document).ready(function() {
$("#InstitutionsSearch").typeahead({
name: 'Institutions',
remote: 'profile/account_names_autocomplete/?q=%QUERY'
What do I need to do with remote to set the search on a field in my DB? I read other threads but it isn't clear to me what remote actually do. Do I have to engage Bloodhound to make this work?
The query is reach my View printing hello0 but no more.
My view:
class UserAccountsUpdate(UpdateView):
context_object_name = 'variable_used_in `add_user_accounts.html`'
form_class = AddUserAccountsForm
template_name = 'add_user_accounts.html'
success_url = 'summary.html'
print "hello0"
def account_name_autocomplete(request):
print "hello1"
query = request.GET.get('q','')
if query:
print "hello2"
results = Insitutions.objects.filter(name__istartswith=query)
result_list = []
for item in results:
result_list.append(item.short)
else:
result_list = []
response_text = json.dumps(result_list, separators=(',',':'))
return HttpResponse(response_text, content_type="application/json")
#get object
def get_object(self, queryset=None):
return self.request.user
Thanks

Web text input save into database without form- Django Javascript

I have an sqlite3 database setup with django 1.6 where I want web users to be able to enter text and see it in 3 live tables (high, low and latest) all on the same page. The one page ideally should have text entry, voting, display of the three tables updated when new entries go in or are voted on, and a search if possible if all that gets sorted (personal project, not commercial). I am also on win7 64 if that matters...
Currently I have: a working database the three tables displaying in a web page without update and some web text input via js (and failing to save to the database in Django).
I shied away from forms at first, as they seem to want separate html pages for input. Asking an experienced django coder, he helped me out with some javascript for text entry on the page. He said I didn't need to do it via forms and POST, GET, as the text was just going in with the initial score of 0 and the current datetime.
My issue now is that I cannot get the entered text to save into the database without error.
Since I need to program this in 2-3 weeks and am new to django (and oblivious to javascript, though I've done some Processing with PHP), my questions are;
Am I missing something obvious with the text input save to database?
-and-
Is there a way to have all this using forms and GET, POST in one page
so I can avoid a lot of javascript (unless it is truly easier)?
I am going to start to try to build this with Forms at the moment, but hope for a bit of guidance on best practice from wiser heads.
Here's the code so far:
urls.py
from django.conf.urls import patterns, include, url
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
url(r'^i/$', 'entries.views.index'),
url(r'^add/(.*)$', 'entries.views.add'),
)
Models.py
from django.db import models
import datetime
from django.utils import timezone
class Entry(models.Model):
text = models.CharField(max_length=15)
score = models.IntegerField(default=0)
pub_date = models.DateTimeField('date published')
def __unicode__(self):
return self.text
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
was_published_recently.admin_order_field = 'pub_date'
was_published_recently.boolean = True
was_published_recently.short_description = 'Published recently?'
index.html
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
</head>
<body>
<ul>
{% for entry in latest_entry_list %}
<li>{{ entry.text }} &nbsp{{ entry.score }}</li>
{% endfor %}
</ul>
<ul>
{% for entry in high_entry_list %}
<li>{{ entry.text }} &nbsp{{ entry.score }}</li>
{% endfor %}
</ul>
<ul>
{% for entry in low_entry_list %}
<li>{{ entry.text }} &nbsp{{ entry.score }}</li>
{% endfor %}
</ul>
<style type="text/css" media="screen">
div h2 span { color: #ff0000; }
div span { color: #00ff00; }
#box { width: 400px; height: 400px; }
#h { color: #ff0000; }
</style>
<h3 id="h">title</h3>
<p>message: {{ text }}</p>
<input type="text" name="word" value="" id="input"/>
<script type="text/javascript" src="{{STATIC_URL}}post.js"></script>
</body>
post.js
console.log("hi from js");
$(document).ready(function() {
$("#input").bind("keypress", function(e) {
//enter key pressed
if (e.keyCode == 13) {
var args = {};
var text = $("#input").val();
$.get("/add/" + text, args).done(function(data) {
console.log("message: " + data);
});
}
});
});
views.py
from django.shortcuts import render
from django.http import HttpResponse
from entries.models import Entry
from django.db import models
import datetime
from django.utils import timezone
def index(request):
context = {
'latest_entry_list': Entry.objects.order_by('-pub_date')[:10],
'high_entry_list': Entry.objects.order_by('-score')[:10],
'low_entry_list': Entry.objects.order_by('score')[:10],
}
return render(request, 'entries/index.html', context);
def add(request, thingtoadd):
#created_date = models.DateTimeField('date published', default=datetime.now)
#created_score = '0'
#created_text = 'test'
#e = Entry(text=created_text, score=created_score,pub_date=created_date)
#e.save()
return HttpResponse('done')
I am unsure of defining the fields for populating the Entry....does the above look right?
I can uncomment the e=Entry(etc...) without error,
but when I uncomment the e.save(), the error is:
GET http://127.0.0.1:8000/add/a 500 (INTERNAL SERVER ERROR) jquery.min.js:4
send jquery.min.js:4
n.extend.ajax jquery.min.js:4
n.(anonymous function) jquery.min.js:4
(anonymous function) post.js:15
n.event.dispatch jquery.min.js:3
r.handle
I will be getting on with trying to do this in forms, but wonder if there is some good advice as to if that is possible - I would ideally like to avoid js extras as I am very unfamiliar with it and it would be another level of unknowns at this point. Any input greatly appreciated...
Your mistake in view function add:
created_date = models.DateTimeField('date published', default=datetime.now)
It must be value assign:
created_date = datetime.now()
Not field definition.
In advance you could specify auto_now_add=True in your model: https://docs.djangoproject.com/en/dev/ref/models/fields/#datefield
In that case field will be filled automatically.
Additional:
It is error in urls.py
You should do some fixes:
urls.py:
url(r'^add/$', 'entries.views.add'),
post.js
$("#input").bind("keypress", function(e) {
//enter key pressed
if (e.keyCode == 13) {
var text = $("#input").val();
var args = {'text': text};
$.get("/add/", args).done(function(data) {
console.log("message: " + data);
});
}
});
views.py
def add(request):
created_date = default=datetime.now()
created_score = '0'
created_text = request.GET.get('text')
e = Entry(text=created_text, score=created_score,pub_date=created_date)
e.save()
return HttpResponse('done')
Update - Solution
The solution in addition to the changes below was to add 'from datetime import datetime' in views....

Categories