Adding Alpine attributes to Django's form fields - django

I'm rendering django template form fields this way :
<div x-data="{ amount: 0 }">
{% render_field field class=class %}
</div>
I want to add to the input : x-on:input.change="console.log('test');"
How can I add this to render_field ?
In forms.py as widgets = { 'amount': forms.TextInput(attrs={'x-on:input.change': "console.log('test');" }) } the only way ?
Is there a way to add x-on:input.change via JavaScript ?

You can add:
attrs={'onChange':"yourJavaScriptFunction(this.id)"} #You can pass arguments about 'this', for example.
Then just add the function in your template, or js.

Related

WTForms how to use description paramter

I have some simple boolean field and I want to add a short description.
I specified a description parameter but it seems like no effecr.
How it suppose to work at all?
from wtforms import BooleanField, Form
class AdminUsersForm(Form):
foo = BooleanField(label='foo', default=True, description='foo')
<form method="GET">
<div>
{{ form.foo.label }}
{{ form.foo(placeholder=checkbox.description)}}
# No description impact at all, even tooltip.
</div>
</form>
I found this answer but not sure that it's my case
You almost have it. Try changing your 'placeholder' to display the text that you have defined:
{{ form.foo(placeholder=form.foo.description)}}
You access this the same way as the form.label, simply by calling the parameter of the field of the form.

WTForms - Pass argument to form then create fields if variable exists

Depending on the arguments I pass to the form I want to return different form fields.
class TestForm(FlaskForm):
"""Test Form."""
if one:
field1 = StringField('Field 1')
if two:
field2 = StringField("Field 2")
if three:
field3 = StringField("Field 3")
submit = SubmitField("Add Service")
def __init__(self, one=None, two=None, three=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.one = one
self.two = two
self.three = three
I am not able to see the arguments when doing the if statements.
I am aware of the option to have logic in html when rendering the form, however due the nature of the project have opted to use quick_form on the html side.
Here is the html code I am using.
{% import 'bootstrap/wtf.html' as wtf %}
<h3 >Add Service</h3>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
</div>
Instead of creating this logic in your form class. I would recommend to create all the fields you need and then dynamically choose which to show the user using jinja2 in your html file.
Here's an example.
{% for fields in fields_list %}
{% if field == '1' %}
{{ form.field1.label(class="form-control-label") }}
{{ form.field1(class="form-control form-control-lg") }}
{% endif %}
{% if field == '2' %}
{{ form.field2.label(class="form-control-label") }}
{{ form.field2(class="form-control form-control-lg") }}
{% endif %}
{% if field == '3' %}
{{ form.field3.label(class="form-control-label") }}
{{ form.field3(class="form-control form-control-lg") }}
{% endif %}
{% endfor %}
And then when you render or redirect to your .html from your routes code, don't forget to send
the proper arguments, such as
# create your fields list, which do I want to show?
# Make it a list of integers
fields_list = [1, 2, 3]
return render_template('insert-name.html', fields_list , form=form)
If my answer helped you, please consider accepting my answer.
I am new to this site and trying to build up some reputation :)
Thank you! Happy Coding!
If someone ever comes accross the same question, here is a quick solution I came upon some times ago.
It is in part inspired from the accepted answer here.
In your forms.py or wherever you declare your TestForm class, put the class declaration inside a function that takes your parameter as an argument and returns the class object as output.
The argument will now be accessible within the class itself, allowing for any test you may want to perform.
Here's a working example based on your original question (I just added a default value to get at least one StringField in case the parameter is ommited):
def create_test_form(var='one'):
class TestForm(FlaskForm):
"""Test Form."""
if var == 'one':
field1 = StringField('Field 1')
if var == 'two':
field2 = StringField("Field 2")
if var == 'three':
field3 = StringField("Field 3")
submit = SubmitField("Add Service")
return TestForm()
Then simply create the form in your routes like so:
form = create_test_form('two')
Finally pass it to your HTML to render the form with quick_form like you did.
This example will render a form with a single StringField named "Field 2" and a "Add Service" submit button.

How to remove "Delete" checkbox on "extra" forms in Django inline formset

I'm using inline formsets in Django, and for each item showing one "extra" form, for adding another object.
The forms for existing objects have "Delete" checkboxes, for removing that object, which makes sense.
But also the "extra" forms have these "Delete" checkboxes... which makes no sense because there's nothing there to delete. Inline forms in the Django admin don't show these "Delete" checkboxes for "extra" forms.
How can I remove these checkboxes on the "extra" inline forms?
The inline formsets part of my template is something like this (simplified, full version on GitHub):
{% for bookimage_form in form.forms %}
{% for hidden_field in bookimage_form.hidden_fields %}
{{ hidden_field.errors }}
{% endfor %}
{{ bookimage_form.as_table }}
{% endfor %}
And here's the "Delete" checkbox that seems superfluous:
You can use the can_delete setting of the InlineModelAdmin class (TabularInline inherits from InlineModelAdmin):
class BookImageInline(admin.TabularInline):
model = BookImage
extra = 1
can_delete = False
Update for Django 3.2+ (link), you can now pass in can_delete_extra as False to formset_factory or it extended classes to remove checkbox from extra forms
can_delete_extra New in Django 3.2.
BaseFormSet.can_delete_extra
Default: True
While setting can_delete=True, specifying can_delete_extra=False will
remove the option to delete extra forms.
For anyone having Django version under 3.2 and do not wish to upgrade, please use the following method of overriding the BaseFormSet:
class CustomFormSetBase(BaseModelFormSet):
def add_fields(self, form, index):
super().add_fields(form, index)
if 'DELETE' in form.fields and form.instance.pk: # check if have instance
form.fields['DELETE'] = forms.BooleanField(
label=_('Delete'),
widget=forms.CheckboxInput(
attrs={
'class': 'form-check-input'
}
),
required=False
)
else:
form.fields.pop('DELETE', None)
YourFormSet = modelformset_factory(
formset=CustomFormSetBase,
can_delete=True,
extra=2
)
only took them 13 years to add this >.> https://code.djangoproject.com/ticket/9061
I found a way to remove the delete checkbox in the official django documentation here
you just have to add 'can_delete=false' as an argument to inlineformset_factory in your views.py file
inlineformset_factory(can_delete=false)
here's a way to get there in the template when you're looping through forms:
{% if bookimage_form.instance.pk %}
<small><b>{{ bookimage_form.DELETE.label_tag }}</b></small><br>
{{ bookimage_form.DELETE}}
{% else %}
{% endif %}
you won't be able to use the as_table() method I don't think, though. You'll have to express every other field in the form.
Here's another thing you could try out after you initialise the form, but before it goes into the context:
for f in form.forms:
if not f.instance.pk:
f.fields['DELETE'] = None
Not sure how that'll come out in the table, but you may be able to monkey with the idea.
My suggest is to render the template in nested for loops and add this:
{% if forloop.parentloop.last and forloop.last%}
not render form filds
{% else %}
render field
{% endif %}

Wagtail not pulling through custom field panels

I'm overriding the wagtail AbstractFormField panel attribute in the following way:
...
before_input = RichTextField(verbose_name=_('before input'), blank=True)
after_input = RichTextField(verbose_name=_('after input'), blank=True)
panels = [
FieldPanel('label'),
FieldPanel('before_input'),
FieldPanel('after_input'),
FieldPanel('required'),
FieldPanel('field_type', classname="formbuilder-type"),
FieldPanel('choices', classname="formbuilder-choices"),
FieldPanel('default_value', classname="formbuilder-default"),
]
where the other panels are what comes out of the box.
This is working perfectly on the admin side and also saving as rich text into my database
I am pulling this through to my form in my template in the following way:
<form action="{% pageurl page %}" method="POST" class="lm-ls1" id="feedback-form">
{% csrf_token %}
{{ form.question1.help_text }} <!-- Simpler non interable way -->
{{ form.question1.before_input }}
<p>---------------</p>
{% for row in form.fields.values %}
{{row.choices}}
<p>---------------</p>
{{row.help_text}}
<p>---------------</p>
{{row.before_input}}
{% endfor %}
</form>
But I am only getting html output for the form panels excluding the before_input and after_input ones
I am getting through roughly the following:
Overall, how did you feel about the service you received today?
---------------
[('Very satisfied', 'Very satisfied'), ('Satisfied', 'Satisfied'),
('Neither satisfied nor dissatisfied', 'Neither satisfied nor dissatisfied'), ('Dissatisfied', 'Dissatisfied'), ('Very dissatisfied', 'Very dissatisfied')]
---------------
Overall, how did you feel about the service you received today?
---------------
---------------
How can I access the before_input field panel data stored in the _formfield wagtail table?
Bit late but hopefully this still helps you or someone else out there.
How Wagtail Forms Work
Wagtail forms provided to the view context for AbstractFormPage models is a fully instanced Django Form. This means that you will only ever find values in the form that can be given to a Django Form.
This includes fields, which are instances of Django's Fields (eg. CharField) and there is no simple way to add additional attributes to these fields.
You can see how the Form object is built in the Wagtail FormBuilder class definition.
1 - Make a Custom Template Tag
A somewhat simple way to get additional attributes on your FormField (Wagtail's FormField) is using a template tag.
Create a new file in in a folder templatetags in your app, and build a simple_tag that will take the form_page, the field (which will be a Django Field instance) and a string of the attribute name you want to get.
# myapp/templatetags/form_tags.py
from django import template
from django.utils.html import mark_safe
register = template.Library()
#register.simple_tag(name='form_field_attribute')
def form_field_attribute(form_page, field, attribute_name, default=None):
"""Return attribute on FormField where field matches 'field' provided."""
# field is a django Field instance
field_name = field.name
results = [
# if html is stored, need to use mark_safe - be careful though.
mark_safe(getattr(form_field, attribute_name, default))
# get_form_fields() is a built in function on AbstractFormPage
for form_field in form_page.get_form_fields()
# clean_name is property on AbstractFormField used for Django Field name
if form_field.clean_name == field_name]
if results:
return results[0]
return default
2 - Revise your form_page.html Template
In your template, cycle through your form (this is the Django Form instance) and use the template helper to get you the extra attributes you need. Example below, passing in page or self will work the same as they are both the instance of your FormPage.
<form action="{% pageurl page %}" method="POST" role="form">
{% csrf_token %}
{% for field in form %}
<div>{% form_field_attribute page field 'before_input' %}</div>
{{ field }}
<div>{% form_field_attribute page field 'after_input' %}</div>
{% endfor %}
<input type="submit">
</form>

dynamic form in Flask

in a movie rating app,I would like to generate a WTF form in flask with dynamic number of fields. i.e, if there are three movies, there will be three fields.
I thought about a few options, but none of them worked:
class RatingForm(Form):
rating = TextField("rating",[validators.Length(min=1, max=1)])
movie_order=TextField("movie",[validators.Length(min=1, max=1)])
submit = SubmitField("submit rating")
pass a parameter to the form object - I don't see how can I pass a parameter to this kind of class
make a loop inside the template, thus generate and return multiple forms, and choose the correct one. this also doesnt work, since the request.form is immutableDict, and I end up having multiple fields with the same key, which I cant access.
{% for movie in movies_to_rate %}
<p>
<form method="POST" enctype="multipart/form-data" action="/rate">
{{ movie}}
{{ forms[movie].rating}}
{{ forms[movie].submit }}
<input type="submit" value="Go">
</p> {% endfor %}
any ideas about what can I do?
I think you can generate a list of TextField's as a class member instead of using one field object. (Though it looks a bit weird, I assume your validators are what you meant.)
class RatingForm(Form):
def __init__(self, count):
self.ratings = [TextField("rating_" + str(i), [validators.Length(min=1, max=1)])
for i in range(count)]
...