So earlier I asked a question about removing the label that Django forms have by default. That worked out great, and I removed the label. However, the text that is generated by the form is still there! I would very much like to remove the text. Here is what I mean:
<p>Text: <textarea rows="10" cols="40" name="text"></textarea></p>
I would like to remove the Text: part of this, as I do not want it. Again, it is generated with the form I create via:
{{ form.as_p }}
Here is the model I use for my form:
class CommentForm(forms.Form):
comment = forms.CharField(widget=forms.Textarea())
EDIT: So far, I've looked at all of the documentation about the label tag and what stuff Forms generate. Apparently, this is possible to remove, it just does not tell me how. Also, I can remove the colon by adding:
label_suffix=None
I have now also tried label, label_tag, label_prefix, prefix, both in the form constructor and the charField constructor. Nothing.
As a parameter in the constructor, but this is not enough.
Anyone know how to fix this one?
EDIT2: I have changed around how the form is done:
class CommentForm(forms.Form):
comment = forms.Textarea()
It's only that now. This means the Textarea is the problem. What parameter can I pass in the textarea or to the form that will remove the aforementioned problem?
The answer:
class CommentForm(forms.Form):
comment = forms.CharField(widget=forms.Textarea(), label='')
Also, no auto_id in the constructor when creating the object, it should be left as:
comment = new CommentForm()
Have you tried:
class CommentForm(forms.Form):
comment = forms.CharField(widget=forms.Textarea(), label=None)
?
Try:
class CommentForm(forms.Form):
comment = forms.CharField(widget=forms.Textarea(), help_text="")
Related
I am trying to implement a tagging process for profiles so you can add your hobbies for example.
I have chosen django-taggit as it seemed quite simple and does what I need it to, plus don't really know how to do it myself from scratch.
I have managed to make it work to some extent but I am having issues with 3 things:
Not really sure what's the best way to control the form field for these tags as I generate the form automatically with widget adjustments in meta function of the form, but it might work fine after resolving the below two issues.
When there is no data for the field hobbies (tags) the field gets populated with a single tag of value "[]" as per below image.
When I add a tag of "music" and submit the form after I reload the page I get this "[]" as per image. I assumed this will be dealt with by the library, but I cannot see another similar scenario online.
When I try adding another tag of "games" and save and reload, the below happens. The initial value gets wrapped again.
My model is:
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
hobbies = TaggableManager()
My form is:
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['hobbies',]
def __init__(self, *args, **kwargs):
super(UserProfileForm, self).__init__(*args,**kwargs)
self.fields['hobbies'].widget = forms.TextInput()
self.fields['hobbies'].widget.attrs['data-role'] = "tagsinput"
self.fields['hobbies'].widget.attrs['class'] = "form-control"
self.fields['hobbies'].required = False
My view function is:
if request.method == 'POST':
user_profile = UserProfile.objects.get(user=request.user)
form = UserProfileForm(request.POST, instance=user_profile)
print(form)
if form.is_valid():
obj = form.save(commit=False)
obj.user = request.user
obj.save()
print("Form valid")
form.save_m2m()
Using:
<script src="/static/js/tagsinput.js"></script>
<link rel="stylesheet" href="{% static 'css/tagsinput.css' %}" />
I had this exact same problem.
One solution is to apply the data-role="tagsinput" AFTER you turn a list of tags into a comma-separated string for the form.
Here is that solution:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self, **kwargs):
self.fields['tags'].widget.attrs['value'] = ", ".join(list(self.instance.tags.names()))
self.fields['tags'].widget.attrs['data-role'] = "tagsinput"
Output:
As you can see, there's a problem with quotes appearing around tags that are multi-word. It also causes new tags with quotes to be saved to the database.
If double-quotes didn't appear around multi-word phrases, this would be the most elegant solution. If someone solves this in the future, drop a note!
My template is this:
<div class="m-3 p-3 border">
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-primary" type="submit">Save Form</button>
</form>
</div>
I know I can use a template tag to strip the extra quotes from the tag field itself, but then I'd have to go through and create all the form fields manually just to set the tags template tag.
For the time being, my solution is to simply use Javascript and just modify the Meta widgets section of the form.
FINAL ANSWER (for now):
forms.py
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
widgets = {
'tags': forms.TextInput(attrs={
"data-role": "tagsinput",
})
}
custom.js - put this script on the page that loads the form.
document.addEventListener("DOMContentLoaded", function(event) {
let tags_input = document.querySelector('#id_tags');
let tags_input_value = tags_input.value;
let new_value = [...tags_input_value.matchAll(/<Tag:\s*([\w\s]+)>/g)].map(([, m]) => m).join(', ')
tags_input.setAttribute('value', new_value);
}
So all we're doing is modifying the front-end presentation, and leaving all the backend internal forms functionality untouched.
So after quite a few (hundreds) of tests, I finally narrowed down where the issue was and tried to go around it with successful result.
It seems the data got amended into tag objects through tagsinput library I was using. Only when the "data-role" was specified as "tagsinput" in the forms.py the data would already come to html side as those objects and be shown incorrectly. So instead I wanted to keep the data clean and only apply data-role='tagsinput' in the end for visual aspect, which I did using:
var hobbiesTags = document.getElementById("id_hobbies");
if(hobbiesTags){
var att = document.createAttribute("data-role");
att.value = "tagsinput";
hobbiesTags.setAttributeNode(att);
};
And that resulted in the below. Maybe there are better ways to do this, I'm not sure, but it's a pretty clean solution. Share your alternatives.
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.
I am trying to create a comments section using Web2Py/Python, I have created the form with no errors, but when the form submits the comments are not completely added. Can anyone spot something I am missing?
db1.py modal:
db.define_table('products',
Field('Product_Name',requires=IS_NOT_EMPTY()),
Field('Product_Description',requires=IS_NOT_EMPTY()),
Field('Product_Review',requires=IS_NOT_EMPTY()),
auth.signature)
db.define_table('product_comments',
Field('products', 'reference products'),
Field('body', 'text', requires=IS_NOT_EMPTY()),
auth.signature)
default.py controller:
def show():
post = db.products(request.args(0, cast=int))
productDescription = T("Product Description")
productReview = T("Product Review")
back = T("Back")
#commentHeading = T("Comments")
db.product_comments.products.default = post.id
db.product_comments.products.readable = False
db.product_comments.products.writable = False
comments = db(db.product_comments.products==post.id).select()
form = SQLFORM(db.product_comments).process()
return locals()
default/show.html view:
{{extend 'layout.html'}}
<h1>{{=XML(post.Product_Name, sanitize=True)}}</h1>
<h2>{{=XML(productDescription, sanitize=True)}}</h2>
{{=XML(post.Product_Description, sanitize=True)}}
<h2>{{=XML(productReview, sanitize=True)}}</h2>
{{=XML(post.Product_Review, sanitize=True)}}
<h2>Comments</h2>
{{for comment in comments:}}
<div class="well">
{{=comment.created_by.first_name}} {{=comment.created_by.last_name}}
on {{=comment.created_on}} says
{{comment.body}}
</div>
{{pass}}
{{=XML(form, sanitize=True)}}
{{=XML(back, sanitize=True)}}
When the form is submitted, you are selecting the existing comments before processing the form (which inserts the new comment), so the newly submitted comment will not be included in the comments shown on the page. Just reverse the order of the last two lines:
form = SQLFORM(db.product_comments).process()
comments = db(db.product_comments.products==post.id).select()
Also, you should get rid of all of the XML(..., sanitize=True) calls, as they are completely unnecessary. That is for when you need to bypass the default escaping in the template but need to sanitize because the content is untrusted. In this case, you do not need to bypass the escaping of any of the content.
I needed an = inside {{comment.body}} to make it look like {{=comment.body}}. However Anthony's answer is customary if you want the comments section to show the body based on index.
Without it, submitting a comment would post the previous submitted comment (always one behind).
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()
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)