I would like to prepend a $ to an input field for a few of my forms. I don't want the $ sent to the backend as part of the input value. I have found a few stack overflow questions that suggest things like the updates below:
self.fields['buy_price'].localize = True
self.fields['buy_price'].widget.is_localized = True
self.fields['buy_price'].prefix = '$'
But none of these are working for me. I also would prefer to avoid adding 50+ lines of code, such as recommended in this S.O. Answer: How to represent Django money field with currency symbol in list template and plain decimal for edits?
HTML form:
{% url 'update-buy/' offer.id as buy_update_url %}
<form method="POST" action="{{ buy_update_url }}">
<div class="form-group">
<legend class="border-bottom mb-4">Update Offer to Buy a Game</legend>
{% csrf_token %}
{{ form|crispy }}
{{ form.media }}
</div>
<input type="hidden" id="hidden_desired_game_id" name="hidden_desired_game" value="{{ offer.desired_game_id }}">
<div class="form-group">
<button onclick="clearMessage()" class="post-button btn btn-outline-info" type="submit">Update</button>
</div>
</form>
Does anyone have a simple way to do this?
UPDATE:
I updated to use {% crispy form %} instead of {{ form|crispy }}, which is nicely showing the $. But now, the hidden input with id="hidden_desired_game_id" is not included in the POST request. For some reason, when rendering the form (screenshot below), that input is now BELOW the form, not inside it. Any idea how I can still have that included?
EDIT #2: I fixed the above problem by moving the input field higher up in the form. But now it looks like jquery is loading twice or something. There are 2 dropdown arrows on the right side of the Desired game field and it looks ugly. I tried using javascript to manipulate the class and played around with the css_class feature from crispy-forms, but i can't get it to only have one dropdown. Does anyone know how to fix that? Screenshot below
SOLUTION FOUND: FINAL UPDATE:
I was able to fix the above issue of 2 dropdowns by adding this javascript:
window.onload = function () {
$( ".custom-select" ).removeClass("custom-select"); // Remove dupe dropdown
}
Kinda hacky but oh well! Everything is good now
You can make use of Bootstrap Layout objects
forms.py
from crispy_forms.helper import FormHelper
from crispy_forms.bootstrap import PrependedText
class ProductForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
PrependedText('buy_price', '$')
)
instead of {{ form|crispy }} you have to use {% crispy form %}
The form field will look like
Related
I'm making a generic blog while learning Django.
I have an ArticleCreateView and ArticleUpdateView, and I am trying to make a custom template that both views would share.
From what I understand, CreateView and UpdateView use the same template by default (article_form.html), which is the template I'm trying to modify.
I have the following in my models.py:
class Article(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
# def __str__ ...
# def get_absolute_url ...
In my views.py:
class ArticleCreateView(CreateView):
model = Article
fields = ['title', 'body']
template_name = 'article_form.html'
class ArticleCreateView(CreateView):
model = Article
fields = ['title', 'body']
template_name = 'article_form.html'
Having the following in my template article_form.html works:
<form method='post'>
{{ form.as_p }}
<button>
Publish
</button>
</form>
I want to make it more fancy though, with loads of CSS, and a simplified version is:
<form method='post'>
{% csrf_token %}
<fieldset class="fieldset-class">
{{ form.title.errors }}
<input
class="form-class"
type="text"
placeholder="Article Title"
name="{{ form.title.name }}"
value="{{ form.title.value }}"
/>
</fieldset>
<fieldset class="form-group">
{{ form.body.errors }}
<textarea
class="form-control"
rows="8"
placeholder="Article Body"
name="{{ form.body.name }}"
>{{ form.body.value }}</textarea>
</fieldset>
</form>
What I want is a form that:
has placeholders in empty fields (instead of labels)
has model-based error validation (max_length is respected and both fields are required without me having to specify it)
doesn't erase the entered values upon submitting
The above html does the following:
submitting with empty fields will raise the errors, and any entered values will be persisted (good)
empty fields have None instead of placeholders (issue)
Substituting object.title and object.body to form.title.value and form.body.value in the above html does the following:
submitting with empty fields will raise the errors, but also erase the entered values (issue)
empty fields have the right placeholders (good)
I have read the documentation's Built-in template tags and filters, Rendering fields manually, Rendering form error messages, Looping over the form’s fields, and Attributes of BoundField, looked for similar code on SO and GitHub, read the source rendered by {{ form.as_p }}, but I find no simple solution despite having a supposedly common usecase.
The only thing I can think of is to shove {% if %} statements everywhere to choose between placeholder and value, or to get some third-party lib like crispy-forms, but it feels like a more pythonic solution should exist.
As always, I found the answer immediately after posting the question on SO.
The built-in template filter default_if_none solves the issue:
<input
class="form-class"
type="text"
placeholder="Article Title"
name="{{ form.title.name }}"
value="{{ form.title.value|default_if_none:'' }}"
/>
I have a form:
class InspectionReportForm(forms.ModelForm):
class Meta:
model = InspectionReport
fields = ('Date', 'Comment', 'Signature')
labels = {"Date": "Date (YYYY-MM-DD)"}
def __init__(self, *args, **kwargs):
super(InspectionReportForm, self).__init__(*args, **kwargs)
self.fields["Comment"].widget = forms.HiddenInput()
self.fields["Date"].widget = forms.HiddenInput()
self.fields["Signature"].widget = forms.HiddenInput()
Which stores a report, in addition to the date and who wrote it.
In the case of a "Pass" or "Fail" report. I want a generic comment in the report, so this would be auto-filled, which is why I have hidden this field. Date and Signature will be taken from the date of submission and the signature will be the logged in user. So mostly all auto-filled.
However I have a "Maybe" option which prompts the user for a comment and submits that instead of the generic ones.
My question is how do I change the comment value of the form from the HTML Template/Javascript stage of my project?
My current code is as follows:
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<center><button type="button" id="pass">Pass</button>
<button type="button" id="fail">Fail</button>
<button type="button" id="check" class="collapsible">Check</button>
<div class="content"> Comment: <input type="text" name ="comments">
<button id="submitCheck">Submit</button>
</div>
</center>
The Check button is just a collapsible container that has an input box and a submit button for the "Maybe" option.
As far as Javascript I have nothing really because I can't directly change the field value from here. I've tried selecting the field with {%for field in form%} but had no luck
You could do the "dirty trick" of copying the contents of your comment field into the hidden Comment field using javascript:
$("input[name=comments]").on("change", function(){
$("input[name=Comment]").val($(this).val());
})
Or, what I believe is a better approach, don't use {{ form.as_p }} but render the fields separately so that you can render the {{ form.Comment }} field directly inside your collapsible. Then you don't have to do anything in javascript. You can render a field's label, the field itself and the field errors like this:
{{ form.Date.label_tag }}
{{ form.Date }}
{{ form.Date.errors }}
How can I access if a field has)errors at the level of widget?
Using default I tried:
{% if widget.attributes.has_errors %} or {% if widget.has_errors %}
but are not working.
I use custom widget templates, I'm thinking to use a custom form Field and overwrite the default field.
I know clean method exist but I don't know how to push to the widget the dynamic(non default) data/attributes I want.
I tried:
class AWidget(forms.Widget):
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
has_errors = context['widget']['attrs'].pop('has_errors', None)
context['widget']['has_errors'] = has_errors
It works for errors but I don't know if is the best option plus I want to pass other values/attributes from Form Field,and I think will be better to try to overwrite the Form Field but I don't know exactly how.
Also accessing individual attributes using:
{{ widget.attrs.maxlength }} or {{ widget.attrs.items.maxlength }}
even if accedes in a for loop works
I know I can add a parent div with a class of error:
<div class="{% if form.field.errors %}pass_error{% endif %}">
{{ form.field }}
</div>
but, that implies big changes at the css level.
I already overwrite all Django widgets with custom widgets, on error I don't need just to change a border color, but to show or not different elements of the widget template and the position of some of them change.
I already modify the based widget to add errors, but I'm looking to do it in a more elegant way at the field level by passing from the field to the widget, parameters depending on error type.
So my question is what I need to overwrite to pass from field to widget errors and other variables ?
Not sure whether this could help in your specific use case ... but just in case, please note that when you build your form in the view, you can add extra parameters as needed, then pass them down to your custom widget.
Working example:
file "forms.py"
from django import forms
def build_ingredient_form(unit):
"""
Ingredient form factory
Here we build the form class dynamically, in order to acces 'unit' via closure.
References:
http://stackoverflow.com/questions/622982/django-passing-custom-form-parameters-to-formset#623030
"""
class IngredientForm(forms.Form):
#quantity = forms.DecimalField(max_digits=10)
quantity = UnitField(unit, required=False)
...
return IngredientForm
file "fields.py"
from django import forms
from .fields import UnitField
class UnitField(forms.CharField):
"""
Custom field to support UnitWidget
References:
- http://tothinkornottothink.com/post/10815277049/django-forms-i-custom-fields-and-widgets-in
"""
def __init__(self, unit, *args, **kwargs):
self.unit = unit
super(UnitField, self).__init__(*args, **kwargs)
self.widget = UnitWidget(unit)
...
file "widgets.py"
from django import forms
from .models import Unit
class UnitWidget(forms.TextInput):
def __init__(self, unit, attrs=None):
if unit is None:
self.unit = Unit()
else:
self.unit = unit
...
Well a widget is how you will render the field's data/value into the HTML rendered template, that's the only function of widgets, look the following example taken from the docs:
>>> name = forms.TextInput(attrs={'required': True})
>>> name.render('name', 'A name')
'<input name="name" type="text" value="A name" required>'
>>>
>>> name = forms.TextInput(attrs={'required': False})
>>> name.render('name', 'A name')
'<input name="name" type="text" value="A name">'
So, widgets are not aware of the data is valid(has errors) or not and should remain that way.
Is not a good idea to handle any data error/validation at the widget level, you want, I can ensure that, that if you change how your field looks like (the widget), your validations keeps working.
Said that ...
How can I access field errors?
When you are rendering a form you can do it field by field lets take this form by example:
class LoginForm(forms.Form):
username = forms.CharField(max_length=255)
password = forms.CharField(widget=forms.PasswordInput)
you can write to temlate:
<form action="." method="get">
<p>{{ loginform.username.label }}: {{ loginform.username }}</p>
<p>{{ loginform.password.label }}: {{ loginform.password}}</p>
<button type="submit">submit</button>
</form>
And this will render something like the following:
Now, suppose your form won't admit passwords with less than 8 characters:
class LoginForm(forms.Form):
username = forms.CharField(max_length=255)
password = forms.CharField(widget=forms.PasswordInput)
def clean_password(self):
password = self.cleaned_data['password']
if len(password) < 8:
raise forms.ValidationError(
"Password must have at least 8 characters, it has only %(password_length)s",
code='invalid password',
params={'password_length': len(password)}
)
return password
You can access the password errors like this:
<form action="." method="get">
{% csrf_token %}
<p>{{ form.username.label }}: {{ form.username }}</p>
<p>{{ form.password.label }}: {{ form.password}}</p>
<ul>
{% for error in form.password.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
<button type="submit">submit</button>
</form>
And now if you type a short password ...
I want the control to look different if there are errors.
You can add some style if there are errors just use {% if ... %} in your template code:
<p>
{{ form.password.label }}:
<span class="{% if form.password.errors %}pass_error{% endif %}">
{{ form.password }}
</span>
</p>
With CSS:
<style>
.pass_error input {
border-color: red;
}
</style>
And this is the result:
Conlusion.
Validate and handle data errors in the form or using validators, use widgets for display the data, of course, you can customize how the data is presented since you can specify a custom template for your widget.
I also recommend django-widget-twaeks if you want to add attributes to your widget in template code. This apps allows you to write code like (example from the app docs):
{% load widget_tweaks %}
<!-- change input type (e.g. to HTML5) -->
{% render_field form.search_query type="search" %}
<!-- add/change several attributes -->
{% render_field form.text rows="20" cols="20" title="Hello, world!" %}
<!-- append to an attribute -->
{% render_field form.title class+="css_class_1 css_class_2" %}
<!-- template variables can be used as attribute values -->
{% render_field form.text placeholder=form.text.label %}
I have a form
Field Name: [Input Box]
I want
Field Name:
[Input Box]
How can I achieve this?
forms.py
class SearchForm(forms.Form):
search = forms.CharField()
views.py
form = SearchForm()
html_dtc = {'form':form}
return render_to_response('site/home.html', html_dtc)
home.html
<form method='POST' action=''> {% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-success btn-sm">Update</button>
</form>
Thank you :)
You want a custom form rendering. You can read more about it here. For example, the following code would achieve what you're after.
<form method='POST' action=''> {% csrf_token %}
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} <br/>
{{ field }}
</div>
{% endfor %}
<button type="submit" class="btn btn-success btn-sm">Update</button>
</form>
(field.errors are added, because when you are manually rendering the form, you have to take care of error rendering as well)
Try to overide form.as_p()
class SearchForm(forms.Form):
search = forms.CharField()
def as_p(self):
"Returns this form rendered as HTML <p>s."
return self._html_output(
normal_row='<p%(html_class_attr)s>%(label)s <br> %(field)s%(help_text)s</p>',
error_row='%s',
row_ender='</p>',
help_text_html=' <span class="helptext">%s</span>',
errors_on_separate_row=True)
If this is a one off thing you can render your form manually like described here in the documentation.
Otherwise there's the django-floppyforms which gives you great control over how forms and (default) widgets are rendered.
Just define a custom layout, make it the default, use floppyforms custom Form classes (they behave exactly the same) and you're good to go.
As far as I remember some of floppyforms's functionality will also be included in Django 1.9, so look out for that, too.
Use django_crispy_forms: http://django-crispy-forms.readthedocs.org/en/latest/
In the template include {% load crispy_forms_tags %} and for the form:
{% crispy form %}
In addition, you can change the layout of the form easily, by overriding the form_class in the init function of the form:
class ContactForm(forms.Form):
def __init__(self, *args, **kwargs):
super(ContactForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.form_class = 'form-horizontal' # <-- leave out for vertical, or form-inline
self.helper.render_hidden_fields = True
self.helper.layout = Layout(
Div(InlineRadios('applying_for_whom'), css_class="col-xs-12"),
Div(InlineRadios('gender'), css_class='col-xs-12'),
Div('agreed_to_data_transmit', css_class="col-xs-12"),
As a bonus, if you are using bootstrap, set also set CRISPY_TEMPLATE_PACK = 'bootstrap3' so then everything is taken care of automatically for vertical bootstrap.
you can do
<form method='POST' action=''>
{% csrf_token %}
<label>Field Name:</label>
{{ form.search }}
<button type="submit" class="btn btn-success btn-sm">Update</button>
</form>
generally I don't recommend use the HTML code generated by Django, but instead I supply what is needed by the DJANGO form.
but some are required: like the ERRORs, like the CSRF token.
let me add some examples to clarify what I am talking
<form class="*" style="*">
<label /><input name="email" />
<label /><input name="password" />
<form>
basically what I am suggesting is, do not use template tags unless absolute necessary like CSRF.
in this way, you can completely separate the design from the backend logic. you can have front end work indecently on the UI. the interface is the form fields, you have to supply all fields to the backend. like in this case 'email' && 'password' is required at backend
class BaseForm(forms.Form):
def as_custom_table(self):
"Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
return self._html_output(
normal_row = u'<tr%(html_class_attr)s><td class="label_col">%(label)s</td><td class="field_col">%(field)s%(help_text)s</td></tr>',
error_row = u'<tr><td colspan="2" class="error">%s</td></tr>',
row_ender = u'</td></tr>',
help_text_html = u'<br />%s',
errors_on_separate_row = True)
I'm trying to see if I can get django to do the heavy lifting and render a form in the way I want it to render.
The problem here is, there might be one or two fields that need to render slightly differently. Such as a "please confirm you agree to our terms and conditions" check box, which would need to span two columns.
Also some other things, such as error placement, might need changing.
I could override the _html_output method to change error placement, but what about getting an individual field to use a different render method?
I think ultimately I need to revert to manually building the form html in the template, but I'm just wondering how much of it Django could do for me with some minor modifications.
The suggested method will be to use a template like this:
<form action="/contact/" method="post">
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }}: {{ field }}
</div>
{% endfor %}
<p><input type="submit" value="Send message" /></p>
</form>
You can conditionally override specific fields using {{ if field.my_property }}.