Wagtail not pulling through custom field panels - django

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>

Related

Can't make Django ModelForm Select Choice Field work in Template

I tried multiple solutions around but couldn't make my select field work in Django Template. I am beginner and humbly apologize if my question is not up to standard. My select button is not showing any options in django form. I only have two options to show. Is there anyway I can get form field without having iterate through it in template? Using Django 3 and Python 3.8. Help will be much appreciated.
Model:
class User(AbstracUser):
ACC_TYPE = (('Student', 'Student'), ('Teacher', 'Teacher'))
role = models.CharField(max_length=100, choices=ACC_TYPE
.....
Form:
class UserForm(UserCreationForm): #Using django's default form
account_type = forms.CharField(widget=forms.Select(choices=User.ACC_TYPE))
model = User
fields =('.............,
'account_type')
def __init__(self, *args, **kwargs):
super(UserCreationForm, self).__init__(*args, **kwargs)
self.fields['account_type'].choices = [(each[0], each[1]) for each in User.ACC_TYPE]
self.fields['account_type'].required = True
Template:
.......
<select>
{% for opt, val in form.account_type.choices %}
<option value="{{ opt }}">{{ value }}</option>
{% endfor %}
</select>
......
Your logic is fine. You shouldn't be having this issue. Only reason could be and its a long shot...that you are using some bootstrap theme with customized CSS classes in it. Now either you load those custom classes per field or you can use django-crispy-fields that inherits all classes with form-group. You will need to load the form with {{ form|crispy }}. For details please check settings for django-crispy-forms. Remember to load the tag at top of page where you want to use it as per settings {% load crispy_forms_tags %}. Read Docs here.

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)]
...

How edit data from form django admin

I'm learning Django Framework, and I have a question. To help you understand I will try and explain using the example below:
Suppose that we have some table in db as is:
CREATE TABLE names (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100));
And I have the form in Django Admin as is:
<form>
<textarea name="names"></textarea>
<input type="submit" name="sbt" value="Submit">
</form>
User entered something in the input names in the form and submitted it. Then a script catches this data and splits it into an array (str.split("\n")) and in cycle adding to table names!
And I many quetion:
How i can add form to Django Admin?
How i can catch form data and add this data to somethink table in database?
Thanks.
First of all you must create a django model.
Put this code in models.py.
class Names(models.Model):
name = models.CharField(max_length = 100)
Then you must create the admin model.
Put this code in admin.py.
class NamesAdmin(admin.ModelAdmin):
list_display = ['name']
# whatever you want in your admin panel like filter, search and ...
admin.site.register(Names, NamesAdmin)
I think it meet your request. And for split the names you can override save model method and split the names in there. But if you want to have an extra form, you can easily create a django model form.
Put the code somewhere like admin.py, views.py or forms.py
class NamesForm(forms.ModelForm)
class Meta:
model = Names
That's your model and form. So, if your want to add the form to django admin panel you must create a view for it in django admin. For do this create a view as common.
Put the code in your admin.py or views.py.
def spliter(req):
if req.method == 'POST':
form = NamesForm(req.POST)
if form.is_valid():
for name in form.cleaned_data['names'].split(' '):
Names(name = name).save()
return HttpResponseRedirect('') # wherever you want to redirect
return render(req, 'names.html', {'form': form})
return render(req, 'names.html', {'form': NamesForm()})
Be aware you must create the names.html and put the below code in you html page.
{% extends 'admin/base_site.html' %}
{% block content %}
<!-- /admin/names/spliter/ is your url in admin panel (you can change it whatever you want) -->
<form action="/admin/names/spliter/" method="post" >{% csrf_token %}
{{ form }}
<input type="submit" value="'Send'" >
</form>
{% endblock %}
This is your view and your can use it everywhere. But if you want only the admin have permission to see this page you must add this method too your NamesAdmin class.
def get_urls(self):
return patterns(
'',
(r'^spliter/$', self.admin_site.admin_view(spliter)) # spliter is your view
) + super(NamesAdmin, self).get_urls()
That's It. I hope this can help you.

Muliple instances of modelform

I have a list in my template. For each item in the list, I have a {{ modelform }} that contains a checkbox. I can check the box and it updates as should. The problem is that when I check the box for one item and submit, it submits for all of the checkboxes because they are the same in each instance. Is there a way to set up a unique checkbox instance for each item in the list?
Current each modelform checkbox renders the same like this:
<input name="is_solution" type="checkbox" class="is_solution" id="is_solution">
I also tried using
test = request.POST.get('checkbox')
and
test = request.POST.get('checkbox')
thinking that using this I might be able to post an update in my view. I think I am going about this all wrong and I am lost. Essentially, I would like to have a checkbox on a list much like here on stackexchange where you can confirm an answer. Any suggestions?
You have to use form's prefix in the view like (just something unique for each form object):
def foo(request, ...):
objs = Model.objects.filter(...)
forms = []
for i, obj in enumerate(objs):
form = ModelForm(instance=obj, prefix=str(i))
forms.append(form)
...
This will make sure each form has unique identifier, hence you will be able to submit a specific form.
And you can render the forms like usual in the template:
<form ...>
{% csrf_token %}
{% for form in forms %}
{{ form }}
{% endfor %}
</form>

How do I get the value of a BoundField in a django template?

The built in as_html, as_ul, as_p methods on Django forms don't work for me, nor does the built in {{field}} rendering, so I'm trying to write a custom form rendering.
Here's what I have so far:
<input id="id_{{field.html_name}}"
type="text"
name="{{field.html_name}}"
placeholder="{{field.label}}" <!-- "placeholder" is really the only reason I need to do a custom implementation -->
value="{{ XXX }}" <!-- what goes here? -->
maxlength="30" />
The question is, what should go in the value attribute (marked XXX above)?
I've done some looking around and it doesn't appear that BoundField supports a value or data attribute. I'm using ModelForms if it matters
Assuming the field name is "username" and the form name is "user_form", there are two values:
1) Initial:
{{ user_form.initial.username }}
2) Bound:
{{ user_form.username.data }}
The value attribute landed in trunk in 2010. The patch shows how to retrieve the value using the form/data (not simple in a template unfortunately). There are some template tag code snippets in the ticket comments you may find useful.
I tried to find an answer for this question for several hours. The above answer didn't help me.
I found the solution here:
http://djangosnippets.org/snippets/2264/
You need to add new directory: /yourproject/yourapp/templatetags/
In /yourproject/yourapp/templatetags/ place 2 files:
__init__.py - empty file
field_value.py - with the following code:
from django import template
register = template.Library()
#register.simple_tag
def field_value(field):
""" returns field value """
return field.form.initial.get(field.name, '')
At the beginning of your template you nedd to add:
{% load field_value %}
Where you want to output a value of a field you need to add:
{% field_value form.field %}
Or if you already have a "field" variable, then just:
{% field_value field %}
For me it was a field with name "text" of an inline form, so I added the following code
{% field_value inline_admin_form.form.text %}