Change value of a textarea using flask-wtforms - python

I am using flask-wtforms to create a Textarea.
body = TextAreaField('body')
I want to change the value of the textarea which you can do in html like this.
<textarea>other value then default</textarea>
How can I integrate this with flask-wtforms?
My template looks like this:
{{ form.body(rows="20") }} <!--texarea -->
With a simple input field I could do something like this:
{{ form.body(value="other value then default") }} <!-- input field -->
I need to set the default value in the template itself. Because it will have info over the article the page is about.

Weird that 2 years after this question was asked, there's still no standard way to implement this.
What I do is write it out in plain HTML:
<textarea id="body" name="body">{{ value }}</textarea>
This works fine and the textarea is validated normally

With TextArea you have to use the default value so it renders with that.
body = TextAreaField('body', default='Some other value')

You can create a widget that defines an entire alternative html behavior, like so:
in widgets.py
from markupsafe import Markup
from wtforms.widgets import html_params
class BodyWidget(object):
data_template = (
'<textarea %(text)s >other value then default</textarea>'
)
def __call__(self, field, **kwargs):
kwargs.setdefault("id", field.id)
kwargs.setdefault("name", field.name)
template = self.data_template
return Markup(
template % {"text": html_params(type="text", value=(field.data or ""), rows=20, cols=50, **kwargs)}
)
in forms.py
from widgets import BodyWidget
body = TextAreaField('body', widget=BodyWidget())

I used kind of a hack. Using javascript to set the value of the textarea.
<script>document.getElementById('body-input').value="other value then default";</script>
Although not optimal, it works.

Related

Dynamically change form labels in Django/oTree

I have a form label that I want to have variable content. I expose to my template a variable called outgroup which I want to be included in the formfield label. My current (incorrect) attempt looks like this:
{% formfield sent_amount label="How much do you want to send to a "+{{outgroup}} %}
But this obviously doesn't work. What is the correct way to get a variable into the label method?
Why couldn't you do something like this in the view?
def study(request, studyID):
if request.method == 'GET' and request.user.is_authenticated:
form = ContactForm()
form.fields['from_email'].label = "{}, what's your email?".format(.get_full_name())
Use form.fields["your_label_id"] to set it.
When the dynamic content to be inserted in the label is a string, instead of an integer, you only need to add () after referring to the dynamic variable (in the example below, after self.player.type)... Like this:
FIRST go to pages.py:
class Contribute(Page):
form_model = 'player'
form_fields = ['type']
def vars_for_template(self):
return dict(
contribution_label='How many {} do you want to contribute?'.format(self.player.type())
)
SECOND go to the relevant HTML page, Contribute.html
{% formfield player.contribution label=contribution_label %}

Changing Id of django form in for loop

I'm stuck in my code. Need help.
This is my front end. I am rendering forms stored in "form_list".
The problem is that the forms stored are of same type and thus produce input fields with same "id" and same "name".
This is my view:-
#login_required
def VideoLinkView(request):
"""view to save the video links """
current_form_list = []
current_form = None
if request.method == 'GET':
vl = VideoLink.objects.filter(company=CompanyModel.objects.get(owner=request.user))
for link in vl:
current_form = VideoLinkForm(link.__dict__)
current_form_list.append(current_form)
return render(request, "premium/video_link.html", context={'form_list':current_form_list})
This is my html template :-
{% for form in form_list %}
<div class="form-group">
<label for="id_video_link">Video Link:</label>
{{ form.video_link }}
</div>
{% endfor %}
How can I create different "id" and different "name" in each iteration of for loop's input tag, automatically without having knowledge of no form stored in form_list.
I tried {{ forloop.counter}} it didn't worked, perhaps I made some mistake. Also, raw python don't work in template.
Thanks in Advance.
The way you are creating your forms is wrong in two ways. Firstly, the first positional argument is for the values submitted by the user; passing this arg triggers validation, among other things. If you are passing values for display to prepopulate the form, you must use the initial kwarg:
current_form = VideoLinkForm(initial={...dict_of_values...})
However, even that is not correct for your use case here. link is a model instance; you should use the instance kwarg:
current_form = VideoLinkForm(instance=link)
Now, to solve the problem you asked, you could just pass a prefix as well as I originally recommended:
for i, link in enumerate(vl):
current_form = VideoLinkForm(instance=link, prefix="link{}".format(i))
However, now that you have shown all the details, we can see that this is not the best approach. You have a queryset; so you should simply use a model formset.
from django.forms import modelformset_factory
VideoLinkFormSet = modelformset_factory(VideoLink, form=VideoLinkForm, queryset=vl)
current_form_list = VideoLinkFormSet()

Pre-populating a BooleanField as checked (WTForms)

For the life of me, I can't figure out how to pre-populate a BooleanField with WTForms. I have a field called "active". It defaults to being not checked, and it's not required. So I set it up like...
class QuestionForm(Form):
question = TextField('Question', [validators.Required()])
slug = TextField('Slug', [validators.Required()])
active = BooleanField('Active')
Then I have an EDIT PAGE where I display a form for the 'question' I want to edit.
{{ form.question.label }}
{{ form.question(value=q.question) }}
{{ form.active(value=q.active) }} Show this question?
If 'active' is True, I'd like the BooleanField (checkbox) to have the 'checked' attribute. And if False, not to. But I can't even figure out how to make the checkbox have a checked state, when rendering the form, let alone the conditional part.
The only way, I've been able to get it to show up checked is if I add default=True when defining the form. But that's not what I need.
I've tried using 'default', 'initial', 'value', 'selected' while rendering the form with no luck. And I've searched the docs and Google. I think I'm missing something! :)
UPDATE
Here's what my view looks like. Maybe it is the problem?
#mod.route('/q/<slug>/edit', methods = ['GET', 'POST'])
def edit(slug):
form = QuestionForm(request.form, csrf_enabled=False)
q = Question.query(Question.slug==slug).get()
if request.method=='POST':
if form.validate_on_submit():
q.question = form.data.get('question')
q.slug = form.data.get('slug')
q.active = form.data.get('active')
q.put()
return redirect('/questions')
return render_template('questions/edit.html', form=form, q=q)
If you have an object you can use it to populate your form like form = QuestionForm(obj=my_obj). If you only want to set the active attribute use form = QuestionForm(active=True).
A BooleanField defined like:
checkbox = BooleanField('title',
default=True,
render_kw ={'checked':''})
snahor's answer helped after much searching (+1). The google seems weak on this question. I found I needed
<div class="form-group">
{{adminForm.is_admin.label}}
{{adminForm.is_admin(checked=True, class_="form-control")}}
</div>
<div class="form-group">
{{adminForm.is_admin.label}}
{{adminForm.is_admin(checked=False, class_="form-control")}}
</div>
which I have utilised as
<div class="form-group">
{{adminForm.is_admin.label}}
{{adminForm.is_admin(checked=user.is_admin, class_="form-control")}}
</div>
To have the default boolean value as True, you need to set the default to "checked"
Basic fields Basic fields generally represent scalar data types with
single values, and refer to a single input from the form.
class wtforms.fields.BooleanField(default field arguments, false_values=None)
Represents an input type="checkbox". Set the
checked-status by using the default-option. Any value for default,
e.g. default="checked" puts checked into the html-element and sets the
data to True
Source
class QuestionForm(Form):
question = TextField('Question', [validators.Required()])
slug = TextField('Slug', [validators.Required()])
active = BooleanField('Active', default="checked")
I had the same problem, and after hours of searching and reading, the solution was very simple.
form = forms.TestForm(request.form)
form.yourbooleanfield.checked = YourVariable
if request.method=="POST" and form.validate():
print(form.yourbooleanfield.data)
In addition to specifying in the template, you can likewise specify in the class definition
class QuestionForm(Form):
question = TextField('Question', [validators.Required()])
slug = TextField('Slug' , [validators.Required()])
activeChecked = BooleanField('Active', default=True )
activeUnChecked = BooleanField('Active', default=False )
None of these solutions worked for me. There seems to be a bug in WTForms that has not been fixed.
Instead, when the route is called I set the value of the Boolean field after I have initialised the form. This works for me
form = GameCreateForm(request.form)
form.allow_comments.data = True
This worked for me
BooleanField(default="checked")
https://wtforms.readthedocs.io/en/2.3.x/fields/
class wtforms.fields.BooleanField(default field arguments, false_values=None)

Passing variables to template based on db

I am trying to add a feature to my app that would allow me to enable/disable the "Call Me" button based on whether or not I am at [home|the office]. I created a model in the database called setting, it looks like this:
class setting(models.Model):
key = models.CharField(max_length=200)
value = models.CharField(max_length=200)
Pretty simple. There is currently one row, available, the value of it is the string True. I want to be able to transparently pass variables to the templates like this:
{% if available %}
<!-- Display button -->
{% else %}
<!-- Display grayed out button -->
{% endif %}
Now, I could add logic to every view that would check the database, and pass the variable to the template, but I am trying to stay DRY.
What is the best way to do this?
UPDATE
I created a context processor, and added it's path to the TEMPLATE_CONTEXT_PROCESSORS, but it is not being passed to the template
def available(request):
available = Setting.objects.get(key="available")
if open.value == "True":
return {"available":True}
else:
return {}
UPDATE TWO
If you are using the shortcut render_to_response, you need to pass an instance of RequestContext to the function.
from the django documentation:
If you're using Django's render_to_response() shortcut to populate a template with the contents of a dictionary, your template will be passed a Context instance by default (not a RequestContext). To use a RequestContext in your template rendering, pass an optional third argument to render_to_response(): a RequestContext instance. Your code might look like this:
def some_view(request):
# ...
return render_to_response('my_template.html',
my_data_dictionary,
context_instance=RequestContext(request))
Many thanks for all the help!
Write a custom context processor.

How to implement a "back" link on Django Templates?

I'm tooling around with Django and I'm wondering if there is a simple way to create a "back" link to the previous page using the template system.
I figure that in the worst case I can get this information from the request object in the view function, and pass it along to the template rendering method, but I'm hoping I can avoid all this boilerplate code somehow.
I've checked the Django template docs and I haven't seen anything that mentions this explicitly.
Actually it's go(-1).
<input type=button value="Previous Page" onClick="javascript:history.go(-1);">
This solution worked out for me:
Go back
But that's previously adding 'django.core.context_processors.request', to TEMPLATE_CONTEXT_PROCESSORS in your project's settings.
Back
Here |escape is used to get out of the " "string.
Well you can enable:
'django.core.context_processors.request',
in your settings.TEMPLATE_CONTEXT_PROCESSORS block and hook out the referrer but that's a bit nauseating and could break all over the place.
Most places where you'd want this (eg the edit post page on SO) you have a real object to hook on to (in that example, the post) so you can easily work out what the proper previous page should be.
You can always use the client side option which is very simple:
Back
For RESTful links where "Back" usually means going one level higher:
<input type="button" value="Back" class="btn btn-primary" />
All Javascript solutions mentioned here as well as the request.META.HTTP_REFERER solution sometimes work, but both break in the same scenario (and maybe others, too).
I usually have a Cancel button under a form that creates or changes an object. If the user submits the form once and server side validation fails, the user is presented the form again, containing the wrong data. Guess what, request.META.HTTP_REFERER now points to the URL that displays the form. You can press Cancel a thousand times and will never get back to where the initial edit/create link was.
The only solid solution I can think of is a bit involved, but works for me. If someone knows of a simpler solution, I'd be happy to hear from it. :-)
The 'trick' is to pass the initial HTTP_REFERER into the form and use it from there. So when the form gets POSTed to, it passes the correct, initial referer along.
Here is how I do it:
I created a mixin class for forms that does most of the work:
from django import forms
from django.utils.http import url_has_allowed_host_and_scheme
class FormCancelLinkMixin(forms.Form):
""" Mixin class that provides a proper Cancel button link. """
cancel_link = forms.fields.CharField(widget=forms.HiddenInput())
def __init__(self, *args, **kwargs):
"""
Override to pop 'request' from kwargs.
"""
self.request = kwargs.pop("request")
initial = kwargs.pop("initial", {})
# set initial value of 'cancel_link' to the referer
initial["cancel_link"] = self.request.META.get("HTTP_REFERER", "")
kwargs["initial"] = initial
super().__init__(*args, **kwargs)
def get_cancel_link(self):
"""
Return correct URL for cancelling the form.
If the form has been submitted, the HTTP_REFERER in request.meta points to
the view that handles the form, not the view the user initially came from.
In this case, we use the value of the 'cancel_link' field.
Returns:
A safe URL to go back to, should the user cancel the form.
"""
if self.is_bound:
url = self.cleaned_data["cancel_link"]
# prevent open redirects
if url_has_allowed_host_and_scheme(url, self.request.get_host()):
return url
# fallback to current referer, then root URL
return self.request.META.get("HTTP_REFERER", "/")
The form that is used to edit/create the object (usually a ModelForm subclass) might look like this:
class SomeModelForm(FormCancelLinkMixin, forms.ModelForm):
""" Form for creating some model instance. """
class Meta:
model = ModelClass
# ...
The view must pass the current request to the form. For class based views, you can override get_form_kwargs():
class SomeModelCreateView(CreateView):
model = SomeModelClass
form_class = SomeModelForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["request"] = self.request
return kwargs
In the template that displays the form:
<form method="post">
{% csrf token %}
{{ form }}
<input type="submit" value="Save">
Cancel
</form>
For a 'back' button in change forms for Django admin what I end up doing is a custom template filter to parse and decode the 'preserved_filters' variable in the template. I placed the following on a customized templates/admin/submit_line.html file:
<a href="../{% if original}../{% endif %}?{{ preserved_filters | decode_filter }}">
{% trans "Back" %}
</a>
And then created a custom template filter:
from urllib.parse import unquote
from django import template
def decode_filter(variable):
if variable.startswith('_changelist_filters='):
return unquote(variable[20:])
return variable
register = template.Library()
register.filter('decode_filter', decode_filter)
Using client side solution would be the proper solution.
Cancel

Categories