Flask WTF-forms adding select and textarea - python

I am trying to make a flask form which produces the following HTML:
<input type="text" name="title" class="field">
<textarea class="field"></textarea>
<select name="status">
<option value="active">Active</option>
<option value="inactive">Inactive</option>
</select>
So far, since I am new to python, I got this far.
{% from "forms/macros.html" import render_field %}
<form method="POST" action="." class="form">
{{ render_field(form.title, class="input text") }}
My question is, have I got this correct so far for the title field, and assuming I have, could someone please explain how I can get a textarea and selectfield? I have read the docs and I am finding it almost impossible to get my head around it.

In my opinion it's better to define the form not in the template but in the controller.
Example form definition :
class CommentForm(Form):
language = SelectField(u'What You Want', choices=CAN_BE_FILLED_LATER_ON)
code = TextAreaField()
All you need later on is to -
Initialize the form by:
comment_form = CommentForm()
Passing it to the template:
return render_template('post_show.jinja2.html', comment_form=comment_form)
Render the form in the template:
<div class="form-group" id='cg-{{comment_form.email.id}}'>
{{comment_form.email.label(class='col-lg-2 control-label',for=comment_form.email.id)}}
<div class="col-lg-9">
{{comment_form.email(class='form-control')}}
<span class="help-block" id='hl-{{comment_form.email.id}}'></span>
</div>
</div

Related

Django: Pass arguments from other form to submit button

I'm building a simple Django app that lets users track stuff for specific days:
It records entries with a name and a date using the upper form.
<form action="" method="post" style="margin-bottom: 1cm;">
{% csrf_token %}
<div class="form-group">
{{ form.entry_name.label_tag }}
<div class="input-group">
<input type="text" class="form-control" id="{{ form.entry_name.id_for_label }}" name="{{ form.entry_name.html_name }}" aria-label="new entry field">
{{ form.entry_date }}
<div class="input-group-append">
<button type="submit" class="btn btn-primary">Add</button>
</div>
</div>
<small id="{{ form.entry_name.id_for_label }}Help" class="form-text text-muted">This can be anything you want to track: An activity, food, how you slept, stress level, etc.</small>
</div>
</form>
Below the form, there are quick add buttons that let users quickly add a new entry with a specific name. In addition, I'd like to use the date selected in the form above. I.e., if a user sets a date in the upper form but then clicks one of the suggested buttons, it should still use the selected date for adding the new entry.
This is what the code for the suggested buttons currently looks like:
{% if entry_counts and entry_dict|length > 0 %}
<div class="card" style="margin-bottom: 1cm;">
<div class="card-body">
<div class="card-title">Suggested entries</div>
{% for name, count in entry_counts.items %}
<form method="post" action="{% url 'app:add_entry_with_date' name form.entry_date.value %}" style="display: inline-block;">
{% csrf_token %}
<button type="submit" class="btn btn-secondary" name="{{ name }}" style="margin-bottom: 5px;">{{ name }}</button>
</form>
{% endfor %}
</div>
</div>
{% endif %}
I'm trying to access the selected date and pass it to the corresponding view: action="{% url 'app:add_entry_with_date' name form.entry_date.value %}", but it still adds the entry at the default date (today) not on the selected date.
My guess, is that the problem is with <button type="submit" class="btn btn-secondary" name="{{ name }}" style="margin-bottom: 5px;">{{ name }}</button>. Does this just pass name but not the date when submitting?
Here are the relevant URL patterns:
class DateConverter:
regex = '\d{4}-\d{2}-\d{2}'
def to_python(self, value):
return datetime.datetime.strptime(value, '%Y-%m-%d')
def to_url(self, value):
return value
register_converter(DateConverter, 'yyyymmdd')
urlpatterns = [
path('', views.index, name='index'),
path('add/<entry_name>/', views.add_entry, name='add'),
path('add/<entry_name>/<yyyymmdd:entry_date>/', views.add_entry, name='add_entry_with_date'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
So whenever adding a new entry (with or without specific date), my add_entry view is called:
#login_required
def add_entry(request, entry_name, entry_date=datetime.date.today()):
# only works for post
# if request.method == 'POST':
entry_name = entry_name.strip().lower()
entry = Entry.objects.create(name=entry_name, date=entry_date, owner=request.user)
return HttpResponseRedirect(reverse('app:index'))
You're trying to pass the date value as part of the URL,
{% url 'app:add_entry_with_date' name form.entry_date.value %}
however, form.entry_date.value won't have a defined value unless your form is bound before it's passed to the template for rendering. As a result, probably, your add_entry view is being called via the URL pattern add, not add_entry_with_date.
Another challenge with your current code is that you want to have the same date-type input element ({{ form.entry_date }}) serve as the source for different, separate HTML forms (you have the first form for adding entries, and then you have one form for each suggested entry). Changing the value of that input when the page is already rendered in the browser won't update the action URLs for the suggested entry forms—unless you use JavaScript.
I think the quickest way to make your existing code work is to write some JavaScript to manipulate the action attribute for the suggested-entry forms whenever the date input value changes.
Manipulating action attributes looks strange though, and also I believe your view, which should work only for POST requests, should use only POST data and not rely on URL parameters. Therefore I recommend that you use hidden inputs, e.g.
<input type="hidden" name="variable-name" value="temporary-date-value-here">
and then have the JavaScript manipulate these input elements' values instead of the form action attribute. Of course you have to update the view too.
Update: sample JS for synchronizing inputs across forms
HTML:
<html>
<head>
<title>Sample synchronization of inputs across forms</title>
</head>
<body>
<h1>Sample synchronization of inputs across forms</h1>
<h2>Form 1</h2>
<form>
<input class="synchronized-inputs" type="date" name="input_date">
</form>
<h2>Form 2</h2>
<form>
<input class="synchronized-inputs" type="date" name="input_date">
</form>
<script src="sync-inputs-across-forms.js" type="text/javascript"></script>
</body>
</html>
JavaScript (sync-inputs-across-forms.js):
var syncedInputs = document.getElementsByClassName('synchronized-inputs');
Array.from(syncedInputs).forEach((source) => {
source.addEventListener('change', () => {
Array.from(syncedInputs).forEach((target) => {
target.value = source.value;
});
});
});
Note that:
Without the JS, selecting a date in one form won't update the other form's value
As indicated in the original answer, you'd want to use hidden inputs for the suggested-entry forms. To do that, just change type="date" to type="hidden" for the other form. Synchronization will still work as the value is tracked in the (invisible parts of the) DOM.

How do I store values in DataStore in Google Cloud Platform?

So im currently using the Google Guestbook Sample App and completely new to this.
What I want to do is create a textbox that lets the user put in a subject, and a drop down menu that has a list of occasions as well as a message to sign to the guestbook.
Ive added this in the HTML index file as shown below. This works fine and shows the content on the page.
<div class="subject-area">
<label for="subject">Subject:</label>
<textarea id="subject" name="subject" rows="1"></textarea>
</div>
<div class="occasion-area">
<label for="occasions">Choose an Occasion:</label>
<select id="events">
<option value="Christmas">Christmas</option>
<option value="New Year">New Year</option>
<option value="Easter">Easter</option>
<option value="Australia Day">Australia day</option>
</select>
</div>
In my python application I've added new Classes for Subject and Occasion for the datastore.
class Subject(ndb.Model):
subject = ndb.StringProperty(indexed=False)
class Occasion(ndb.Model):
occasion = ndb.StringProperty(indexed=False)
Now I want to store the subject and occasion value into DataStore but when I go to my DataStore it no entity as show in image link 1. Ive tried to get the values but didn't seem to work.
greeting.subject = self.request.get('subject')
greeting.put()
greeting.occasion = self.request.get('occasion')
greeting.put()
Once Ive submitted all of the values(message, subject, occasion) I want to display it all on the page after submitting but not quite sure on how to do that yet?
Heres what my page looks like so far - 2
Not sure if creating a class for both (ocassions and subject) is the right way to go (you might just want to add strings on Greeting class). Anyways, lets focus on Subject for a couple of things you are missing:
You still need to map both classes on the Greeting class:
class Greeting(ndb.Model):
author = ndb.StructuredProperty(Author)
content = ndb.StringProperty(indexed=False)
date = ndb.DateTimeProperty(auto_now_add=True)
subject = ndb.StructuredProperty(Subject)
occassion = ndb.StructuredProperty(Occasion)
Include the assignation on the
def post(self) Post method:
greeting.content = self.request.get('content')
greeting.subject = Subject(
subject=self.request.get('subject'))
greeting.occassion = Occasion(
occasion=self.request.get('event'))
greeting.put()
*Notice the 'event' when creating the "occasion" object, it should match the "name" attribute for the "events" select html tag.
HTML tags should be inside the <form...> and should look like this:
<form action="/sign?guestbook_name={{ guestbook_name }}" method="post">
<div class="subject-area">
<label for="subject">Subject:</label>
<textarea id="subject" name="subject" rows="1"></textarea>
</div>
<div class="occasion-area">
<label for="occasions">Choose an Occasion:</label>
<select id="events" name="event">
<option value="Christmas">Christmas</option>
<option value="New Year">New Year</option>
<option value="Easter">Easter</option>
<option value="Australia Day">Australia day</option>
</select>
</div>
<div><textarea name="content" class="input-block-level" rows="3"></textarea></div>
<div><input type="submit" class="btn btn-large btn-primary" value="Sign Guestbook"></div>
</form>
Print the value on the html with the jinja2 syntax:
<div class="container">
<!-- [START greetings] -->
{% for greeting in greetings %}
<div class="row">
{% if greeting.author %}
<b>{{ greeting.author.email }}
{% if user and user.user_id() == greeting.author.identity %}
(You)
{% endif %}
</b> wrote:
{% else %}
An anonymous person wrote:
{% endif %}
<blockquote>{{ greeting.subject.subject }}</blockquote>
<blockquote>{{ greeting.occasion }}</blockquote>
<blockquote>{{ greeting.content }}</blockquote>
</div>
{% endfor %}
With 1 and 2 you will correctly store the values on Datastore, you can check it out on Console > Datastore > Entities. With 3 and 4 you will be able to interact with the values from the frontend side.

How to append two separate forms to the end of a url in Django

I am attempting to create a django web app, and I'm running into an issue with forms. I have a simple index.html set up that has two separate regular html forms on the page. One for filtering and the other for sorting. Currently, I can get either filtering, or sorting to work, but not both at the same time. I think this is because I'm missing a fundamental understanding of django somewhere. Yes, I've done the tutorials.
I've attempted manually appending the URL to my url config, but that didn't work as intended.
<form action="" method="get">
{% for filter in view.all_filters %}
<label>
<input type="checkbox" name="filter" value="{{ filter }}">
{{ filter }}
</label>
{% endfor %}
<input type="submit" value="Apply Filters">
</form>
<form action="." method="get">
<label>
Sort By
<select name="order_by">
<option value="name">Name</option>
<option value="description">Description</option>
<option value="cvssBaseScore">Cvss Base Score</option>
<option value="cvssV3BaseScore">Cvss V3 Base Score</option>
</select>
</label>
<input type="submit" value="Submit">
</form>
I would like the url to be able to append something like ?filters=one&filters=two&order_by=name or something as such.
The question is not really related to Django but rather to how HTML forms work:
A <form> element encapsulates all the data (<input>s) that it submits, so if you put two separate forms in your HTML page, then you intentionally want only one of the forms to be submitted with its data.
Just put both your inputs (the filter and the order_by inputs) inside the same <form> element so that both are submitted when either of the submit buttons is clicked.

In Django, how do I access template values in the views.py?

I'm fairly new to Django and I'm working on a page that takes in user information. If all of the information is correct, it will proceed to the next page. However, if the user does not provide all given info, it will to refresh the page. My problem is that there are quite a bit of fields the user has to fill out and if the person misses any fields, I don't want them to have to re-type everything out. So my workaround for it is that in the views.py I created a dictionary and it populates it with the input names in the template. However, when I go to run the code, it gives me an error saying that the values in my dictionary do not exist. I'm now thinking that my dictionary is not actually accessing any of the template values.
Here is my template:
<!DOCTYPE html>
{% extends "Checklist/base.html" %}
{% block main_content %}
{% load static %}
<html>
<body>
<form action="{% url 'Checklist:signin_check' %}" method="post">
{% csrf_token %}
<ul style="list-style-type:none">
<li>
<label for="driver_first_name">Driver First Name:</label>
<input type="text" name="driver_first_name" value="" id="driver_first_name">
</li>
<li>
<label for="driver_last_name">Driver Last Name:</label>
<input type="text" name="driver_last_name" value="" id="driver_last_name">
</li>
<li>
<label for="driver_wwid">Driver WWID:</label>
<input type="text" name="driver_WWID" value="" id="driver_WWID" maxlength="8"
onkeypress="return (event.charCode == 8 || event.charCode == 0) ? null : event.charCode >= 48 && event.charCode <= 57">
</li>
<li>
<label for="co_driver_first_name">CO-Driver First Name:</label>
<input type="text" name="co_driver_first_name" value="" id="co_driver_first_name">
</li>
<li>
<label for="co_driver_last_name">CO-Driver Last Name:</label>
<input type="text" name="co_driver_last_name" value="" id="co_driver_last_name">
</li>
<li>
<label for="co_driver_wwid">CO-Driver WWID:</label>
<input type="text" name="co_driver_WWID" value="" id="co_driver_WWID" maxlength="8"
onkeypress="return (event.charCode == 8 || event.charCode == 0) ? null : event.charCode >= 48 && event.charCode <= 57">
</li>
<li>
<input type="submit" value="Continue">
</li>
</ul>
</form>
</body>
</html>
{% endblock %}
Here is the views.py:
def signin_check(request):
driver_info_model = Driver()
if request.method == "POST":
driver_info_form = Driver_Form(request.POST)
c = {'driver_first_name':driver_first_name, 'driver_last_name':driver_last_name,
'driver_WWID':driver_WWID, 'co_driver_first_name':co_driver_first_name,
'co_driver_last_name':co_driver_last_name, 'co_driver_WWID': co_driver_WWID,}
if driver_info_form.is_valid():
driver_info_form.save()
return render(request, 'Checklist/checklist.html')
template = loader.get_template('Checklist/signin.html')
return HttpResponse(template.render(c, request))
any feedback would be greatly appreciated. Thanks!
However, when I go to run the code, it gives me an error saying that
the values in my dictionary do not exist. I'm now thinking that my
dictionary is not actually accessing any of the template values.
From your views.py alone I'm guessing the exception you're running into is that you're assigning dictionary values that aren't defined. For example, in 'driver_first_name':driver_first_name, Python is looking for a variable named driver_first_name but you haven't defined it. The data you're looking for, as Justin alluded to, can be found in requests.POST.
One solution, while more verbose, illustrates what needs to be done:
def signin_check(request):
driver_info_model = Driver()
if request.method == "POST":
driver_info_form = Driver_Form(request.POST)
driver_first_name = request.POST.get('driver_first_name', '')
driver_last_name = request.POST.get('driver_last_name', '')
driver_WWID = request.POST.get('driver_WWID', '')
co_driver_first_name = request.POST.get('co_driver_first_name', '')
co_driver_last_name = request.POST.get('co_driver_last_name', '')
co_driver_WWID = request.POST.get('co_driver_WWID', '')
c = {'driver_first_name': driver_first_name,
'driver_last_name': driver_last_name,
'driver_WWID': driver_WWID,
'co_driver_first_name': co_driver_first_name,
'co_driver_last_name': co_driver_last_name,
'co_driver_WWID': co_driver_WWID, }
if driver_info_form.is_valid():
driver_info_form.save()
return render(request, 'Checklist/checklist.html')
template = loader.get_template('Checklist/signin.html')
return HttpResponse(template.render(c, request))
My problem is that there are quite a bit of fields the user has to
fill out and if the person misses any fields, I don't want them to
have to re-type everything out.
To address your second concern you'll need to deal with your HTML template. Your input fields have a value of "", so any value you pass through your context is not going to reach any of them. Luckily you're on the right path and you're quite close, so all you need to do is fill those values in. For example:
<li>
<label for="driver_first_name">Driver First Name:</label>
<input type="text" name="driver_first_name" value="{{ driver_first_name }}" id="driver_first_name">
</li>
Note that {{ driver_first_name }} is referencing the driver_first_name that's being passed into the context.
Im not 100% sure as i'm fairly new to Django myself, but from what i've done previously you can get the POST data from the request that is passed in, like this:
request.POST['driver_first_name']
which raises an error if no data is present or from
request.POST.get('driver_first_name', 'optionaldefaultvalue')
which returns None if no data is present in the specified field, or an optional default.
It might also be easier to do what you are after with django's inbuilt forms

django modelformset_factory doesn't include actual forms

I've been trying to follow tutorials and other SO questions and have a modelformset_factory that's displaying a list of what looks like forms in the html, but it turns out they're not actual forms.
html that gets displayed:
<div ='container'>
<div class='row'><tr><th><label for="id_form-0-config_key">Config key:</label></th><td><input id="id_form-0-config_key" maxlength="63" name="form-0-config_key" type="text" value="ClientMustVerify" /></td></tr>
<tr><th><label for="id_form-0-config_value">Config value:</label></th><td><input id="id_form-0-config_value" maxlength="63" name="form-0-config_value" type="text" value="TRUE" /><input id="id_form-0-id" name="form-0-id" type="hidden" value="3" /></td></tr> <input type="submit" value="Update" /></div>
<div class='row'><tr><th><label for="id_form-1-config_key">Config key:</label></th><td><input id="id_form-1-config_key" maxlength="63" name="form-1-config_key" type="text" value="Auditing" /></td></tr>
<tr><th><label for="id_form-1-config_value">Config value:</label></th><td><input id="id_form-1-config_value" maxlength="63" name="form-1-config_value" type="text" value="FALSE" /><input id="id_form-1-id" name="form-1-id" type="hidden" value="4" /></td></tr> <input type="submit" value="Update" /></div>
<div>
notice there is no form tag anywhere. working backwards, here's the excerpt from the template:
<div ='container'>
{% for form in formset %}
<div class='row'>{{form}} <input type="submit" value="Update" /></div>
{% endfor %}
<div>
yes, I added the submit button manually hoping to get these to work, but of course if there isn't a form tag, then the submit button won't do anything.
views.py:
from limbo.models import serverConfig
from django.forms import modelformset_factory
from django.forms import formset_factory
def editServer(request):
result = serverConfig.objects.values()
myConfigs = [entry for entry in result]
finalFormSet = modelformset_factory(serverConfig, exclude=('id',), extra=0)
#other lines
return render(request, 'limboHtml/ServerConfiguration.html', {'formset': finalFormSet, 'SubmitMessage': '', 'CurrentConfigs': myConfigs})
forms.py:
class serverForm(ModelForm):
class Meta:
model = serverConfig
fields = ['config_key', 'config_value']
def __init__(self, *args, **kwargs):
super(serverForm, self).__init__(*args, **kwargs)
instance = getattr(self, 'instance', None)
if instance and instance.pk:
self.fields['config_key'].widget.attrs['readonly'] = True
self.fields['config_key'].widget.attrs['disabled'] = True
and models.py:
class serverConfig(models.Model):
config_key = models.CharField(max_length=63)
config_value = models.CharField(max_length=63)
I tried using finalFormSet = formset_factory(serverForm, extra=0) at one point, but then I just got no content in the html...
As described in the formset documention you must add the form tag manually. This is not very different from what you do when displaying a single form.
It appears that you are iterating through the formset and displayig them one by one. That means you must also add the management form
<form method="post" action="">
{{ formset.management_form }}
<div ='container'>
{% for form in formset %}
<div class='row'>{{form}} <input type="submit" value="Update" /></div>
{% endfor %}
<div>
</form>
Or you will get errors about a missing or misconfigured management form.
Note that it does not include the tags, or a submit button. We’ll have to provide those ourselves in the template.
Read more: Working with Forms: Building a form in Django
The reason you are not getting the <form> tag is because from a logical point of view a form validation can be handled anywhere in your application. That's why you need to specify the form tag explicitly with the target url (good to use reverse(view_name)), method and other parameters.

Categories