Writing a Django Form while keeping HTML styling - python

I have a Django form that I have created manually in order to keep the format of the styling, but I realized that the form is compromised of several inputs and manually is taking too long to change each.
I am also able to generate the form automatically using {{ form.as_p }} but I lose the HTML style format that I have below. Is there an easy way to make it instead of manually changing each input?
This is the original HTML template that I am trying to keep
</button>
<div class="form-outline mb-4">
<input
type="text"
id="businessName"
class="form-control"
name="businessName"
/>
<label class="form-label" for="typeText"
>Legal Business Name</label>
</div>
Here is the working Django form:
{% if submitted %}
Your forms has been submitted
{% else %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<!-- Submit button -->
<button
type="submit"
class="btn btn-primary btn-block mb-4"
id="btn"
>
Submit
</button>
</form>
Here is the views.py
def add_form(request):
submitted=False
if request.method == 'POST':
form = infoForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/?submitted=True')
else:
form = infoForm()
if 'submitted' in request.GET:
submitted=True
return render(request, 'template/template.html',{'form':form, 'submitted':submitted})
Here is the form
class infoForm(ModelForm):
class Meta:
model = Info
fields = ['businessName']
Here is what I have tried:
<div class="form-outline mb-4">
<input type="text" class="form-control" name="businessName" {% if form.is_bound %}value="{{ form.businessName.value }}"{% endif %}>
<label class="form-label">Legal Business Name</label>
</div>
{% for err in form.businessName.errors %}
<small class="text-danger mb-2 ml-2">{{ err }}</small>
{% endfor %}
My Question:
How to keep the same HTML styling while making it easy by using {{ form.as_p }}?
What is the required input for attributes in this case?

This will be the quickest way to apply custom styling to the django forms so that you let the django take care of processing the form while still using your preferred css styling...
Taken from my answer to: How to markup form fields with in Django
class MyForm(forms.Form):
myfield = forms.CharField(widget=forms.TextInput(attrs={'class': 'myfieldclass'}))
or
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['myfield'].widget.attrs.update({'class': 'myfieldclass'})
or
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
widgets = {
'myfield': forms.TextInput(attrs={'class': 'myfieldclass'}),
}
originally answered
EDIT 1 : Adding Label styling
1 set the class by the above mentioned method
eg:
self.fields['some_field'].widget.attrs.update({'class': 'some_class'})`
2 Select the label of that class and style them
.that_some_class label{
font-size: large;
}

Related

django modelformset_factory doesn't include actual forms

I've been trying to follow tutorials and other SO questions and have a modelformset_factory that's displaying a list of what looks like forms in the html, but it turns out they're not actual forms.
html that gets displayed:
<div ='container'>
<div class='row'><tr><th><label for="id_form-0-config_key">Config key:</label></th><td><input id="id_form-0-config_key" maxlength="63" name="form-0-config_key" type="text" value="ClientMustVerify" /></td></tr>
<tr><th><label for="id_form-0-config_value">Config value:</label></th><td><input id="id_form-0-config_value" maxlength="63" name="form-0-config_value" type="text" value="TRUE" /><input id="id_form-0-id" name="form-0-id" type="hidden" value="3" /></td></tr> <input type="submit" value="Update" /></div>
<div class='row'><tr><th><label for="id_form-1-config_key">Config key:</label></th><td><input id="id_form-1-config_key" maxlength="63" name="form-1-config_key" type="text" value="Auditing" /></td></tr>
<tr><th><label for="id_form-1-config_value">Config value:</label></th><td><input id="id_form-1-config_value" maxlength="63" name="form-1-config_value" type="text" value="FALSE" /><input id="id_form-1-id" name="form-1-id" type="hidden" value="4" /></td></tr> <input type="submit" value="Update" /></div>
<div>
notice there is no form tag anywhere. working backwards, here's the excerpt from the template:
<div ='container'>
{% for form in formset %}
<div class='row'>{{form}} <input type="submit" value="Update" /></div>
{% endfor %}
<div>
yes, I added the submit button manually hoping to get these to work, but of course if there isn't a form tag, then the submit button won't do anything.
views.py:
from limbo.models import serverConfig
from django.forms import modelformset_factory
from django.forms import formset_factory
def editServer(request):
result = serverConfig.objects.values()
myConfigs = [entry for entry in result]
finalFormSet = modelformset_factory(serverConfig, exclude=('id',), extra=0)
#other lines
return render(request, 'limboHtml/ServerConfiguration.html', {'formset': finalFormSet, 'SubmitMessage': '', 'CurrentConfigs': myConfigs})
forms.py:
class serverForm(ModelForm):
class Meta:
model = serverConfig
fields = ['config_key', 'config_value']
def __init__(self, *args, **kwargs):
super(serverForm, self).__init__(*args, **kwargs)
instance = getattr(self, 'instance', None)
if instance and instance.pk:
self.fields['config_key'].widget.attrs['readonly'] = True
self.fields['config_key'].widget.attrs['disabled'] = True
and models.py:
class serverConfig(models.Model):
config_key = models.CharField(max_length=63)
config_value = models.CharField(max_length=63)
I tried using finalFormSet = formset_factory(serverForm, extra=0) at one point, but then I just got no content in the html...
As described in the formset documention you must add the form tag manually. This is not very different from what you do when displaying a single form.
It appears that you are iterating through the formset and displayig them one by one. That means you must also add the management form
<form method="post" action="">
{{ formset.management_form }}
<div ='container'>
{% for form in formset %}
<div class='row'>{{form}} <input type="submit" value="Update" /></div>
{% endfor %}
<div>
</form>
Or you will get errors about a missing or misconfigured management form.
Note that it does not include the tags, or a submit button. We’ll have to provide those ourselves in the template.
Read more: Working with Forms: Building a form in Django
The reason you are not getting the <form> tag is because from a logical point of view a form validation can be handled anywhere in your application. That's why you need to specify the form tag explicitly with the target url (good to use reverse(view_name)), method and other parameters.

password field is showing password in plain text

I have used django allauth for user registration and login system. I could show the form by simplifying the lines of code using for loop. I got the right field type(TextInput and PasswordInput) for each field too. However the password field which has PasswordInput shows password in plain text. How can i resolve this?
my signup page(account/signup.html)
<form class="signup" id="signup_form" method="post" action="{% url 'account_signup' %}">
{% csrf_token %}
{% for field in form.visible_fields %}
<div class="form-group">
<label for="{{ field.id_for_label}}">{{field.label}}</label>
{{ field.errors.0 }}
<input type="{{field|input_type}}" name="{{ field.name }}" class="form-control" id="{{ field.id_for_label}}">
</div>
{% endfor %}
</form>
filters.py
from django import template
register = template.Library()
#register.filter('input_type')
def input_type(field):
print('field',field.field.widget.__class__)
return field.field.widget.__class__.__name__
How can i show password in dot?
You can add class by overriding __init__ method in form class
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['password'].widget.attrs['class'] = 'form-control'
The password is showing in plain text because you're assigning <input> types incorrectly, therefore not hiding passwords as <input type="password"> does.
From reading the comments, it looks like you're trying to add custom bootstrap classes to the form fields. As Anna Vracheva was saying, you can add the class to the fields using the form's __init__ method.
from django import forms
class CustomForm("""Whichever class you're inheriting from, probably ModelForm"""):
# If you're using AllAuth, this is already defined on the form
password = fields.CharField(widget=forms.PasswordInput)
# Or whatever field, for that matter
def __init__(self, *args, **kwargs):
super(CustomFieldForm, self).__init__(*args, **kwargs)
# Option 1 - Assign to only password
self.fields['password'].widget['class'] = 'form-control'
# Option 2 - Loop over all fields and assign to all
for field in self.fields:
field.widget['class'] = 'form-control'
Then, instead of manually rendering HTML, let Django's Templates do that:
<!-- This -->
{{ field }}
<-- -->
<!-- Instead of this -->
<input type="{{field|input_type}}" name="{{ field.name }}"
class="form-control" id="{{ field.id_for_label}}">
That should fix any field rendering problems you're having while preserving your form classes.
You can also try this:
<input class="form-control" type="{{ field.field.widget.input_type }}"
name="{{ field.name }}"
id="id_{{ field.name }}" >

Django ClearableInputField in form doesn't render in HTML [duplicate]

This question already has an answer here:
Customize the styles of Django ClearableFileInput widget
(1 answer)
Closed 6 years ago.
I'm working on a web app that has photo upload functionality. I created a ModelForm to gather minimal user info plus a photo, and when I render it in HTML as {{ form.as_p }}, the field that allows the user to upload an image shows up just fine. The problem is, the form doesn't look good.
I need to be able to manually render the form in order to make it look better. I have written the HTML for this, and everything looks right except for the ImageFileField. Only the label gets rendered, not the upload button, checkbox to clear the file, etc.
What do I need to do to get the ImageFileField from the ModelForm to render correctly in my custom HTML? I've looked at the Django docs up and down, looked here on SO and can't find anyone else who's had this issue. Many thanks in advance!
forms.py snippet
class PostForm(forms.ModelForm):
class Meta:
model = Items
fields = ('title', 'description', 'image_file')
new_item.html snippet
<form enctype="multipart/form-data" method="post" action="" class="post-form">
{% csrf_token %}
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.title.errors }}
<label for="{{ form.title.id_for_label }}">Title:</label><br>
{{ form.title }}
</div><br>
<div class="fieldWrapper">
{{ form.description.errors }}
<label for="{{ form.description.id_for_label }}">Description: </label><br>
{{ form.description }}
</div><br>
<div class="fieldWrapper">
{{ form.image_field.errors }}
<label for="{{ form.image_field.id_for_label }}">Image (optional):</label><br>
{{ form.image_field }}
</div>
<button type="submit" class="save btn btn-default">Save</button>
</form>
models.py snippet
class Items(models.Model):
title = models.CharField(max_length=1000, null=False)
description = models.TextField(max_length=1000, null=False)
image_file = models.ImageField(max_length=1000,
blank=True,
default='',
null=True,
upload_to='item_photos')
By default django ModelForm uses django.forms.ImageField and not ClearableInputField for django.db.ImageField as revealed at https://docs.djangoproject.com/en/1.9/topics/forms/modelforms/#field-types
And I do believe you actually meant ClearableFileInput
ClearableFileInput¶
class ClearableFileInput File upload input: ,
with an additional checkbox input to clear the field’s value, if the
field is not required and has initial data.
How you can make use of it is by changing the widget in the class meta
class PostForm(forms.ModelForm):
class Meta:
model = Items
fields = ('title', 'description', 'image_file')
widgets = {
'name': ClearableFileInput(),
}
I ended up using the Chrome tool to inspect the HTML source for the page that rendered correctly (but ugly), and used that as a guide to custom build the form in HTML to my liking. This is what I needed to add into my HTML form to get it right:
{% if item.image_file %}
Currently:
{{item.image_file.url}}
<input id="image_file-clear_id" name="image_file-clear" type="checkbox" /> <label for="image_file-clear_id">Clear</label><br />Change: <input id="id_image_file" name="image_file" type="file" /></p>
{% endif %}
{% if not item.image_file %}
<input id="id_image_file" name="image_file" type="file" /></p>
{% endif %}

Django form will not validate

I am working with Django forms and for some reason, this form will not validate! It submits alright, or at least the runserver shows an http post response with code 200 (ok). For some reason though, my form will not pass the is_valid test!
views.py:
def new_show(request):
if request.method == 'POST':
img_form = ImageForm(request.POST, request.FILES)
show_form = NewShowForm(request.POST)
if show_form.is_valid():
new_Show = Show()
new_Show.title=show_form.cleaned_data['title']
new_Show.body=show_form.cleaned_data['body']
new_Show.pub_date=timezone.now()
new_Show.location=show_form.cleaned_data['location']
new_Show.time=show_form.cleaned_data['time']
new_Show.save()
if img_form.is_valid():
image=Image(image=request.FILES['imageFile'])
new_Show.image_set.add(image)
return HttpResponseRedirect(reverse('shows'))
else:
return HttpResponseRedirect(reverse('shows'))
else:
show_form = NewShowForm()
img_form = ImageForm()
return render_to_response(
'shows/new_show.html',
{'show_form': show_form, 'img_form': img_form},
context_instance=RequestContext(request)
)
Here is my template snippet:
<form action="{% url "new_show" %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ show_form.non_field_errors }}</p>
<p>
<label for="title">Title:</label>
<input type="text" name="title"/>
</p>
<p>
<label for="body">Body:</label>
<textarea type="text" name="body"> </textarea>
</p>
<p>
<label for="location">Location:</label>
<input type="text" name="location"/>
</p>
<p>
<label for="time">Date:</label>
<input type="text" id="time" maxlength="25" size="25" name="time"><img src="{{ STATIC_URL }}../../static/cal.gif" width="16" height="16" border="0" alt="Pick a date">
</p>
<!-- Upload Form. Note enctype attribute! -->
{% csrf_token %}
<p>{{ img_form.non_field_errors }}</p>
<p>{{ img_form.imageFile.label_tag }}</p>
<p>
{{ img_form.imageFile.errors }}
{{ img_form.imageFile }}
</p>
<p><input type="submit" value="Add Upcoming Show"></input></p>
</form>
Here is my form Class:
class NewShowForm(forms.Form):
title=forms.CharField()
body=forms.CharField(widget=forms.TextArea)
location=forms.CharField()
time=forms.DateTimeField(required=True)
class ImageForm(forms.Form):
imageFile = forms.FileField(required=False, label='Select an Image')
Please help me!
If new_Show is a model, why not create a ModelForm instead of forms.Form?
So, instead of
class NewShowForm(forms.Form):
title=forms.CharField()
body=forms.CharField(widget=forms.TextArea)
location=forms.CharField()
time=forms.DateTimeField(required=True)
class ImageForm(forms.Form):
imageFile = forms.FileField(required=False, label='Select an Image')
why not using,
from django.forms import ModelForm
class NewShowForm(ModelForm):
class Meta:
model = NewShow
class ImageForm(ModelForm):
class Meta:
model = Image
?
Using ModelForm will ensure that form validation meets that of model. Moreover, it can cut off your code (especially line 6 to 11).
It will help to add these two lines to your view before if is_valid() to see the errors it's giving:
if request.method == 'POST':
img_form = ImageForm(request.POST, request.FILES)
show_form = NewShowForm(request.POST)
print(form.is_valid())
print(form.errors)
if show_form.is_valid():
You can paste the errors here and we can see what's the issue
Since you've put 2 Django forms together under one HTML form tag, when you submit the form on the front-end you're sending an extra field through request.POST that your NewShowForm doesn't have. If you combine both forms into a single Django form, you should be able to get this to work.

Django ValidationError for custom model formset with custom fields (data is missing or has been tampered with)

I'm working on a model formset for a Membership model (models auth.User membership to app.Project). Here's app/forms.py, after I trimming things I have confirmed to be irrelevant after commenting them out:
class EditMembership(forms.ModelForm):
delete = forms.BooleanField(required=False, initial=False)
class Meta:
model = models.Membership
fields = ('is_owner',)
def clean_delete(self):
return self.cleaned_data['delete']
class BaseEditMemberships(forms.models.BaseModelFormSet):
# Some custom methods not dealing with validation.
pass
EditMembershipSet = forms.models.modelformset_factory(models.Membership,
form=EditMembership, extra=0, formset=BaseEditMemberships)
Here is my template:
<form method="{{ method|default:"post" }}" action="{{ action }}">
{% csrf_token %}
<ul>
{% for form in formset %}
<li>{{ form.instance.user}}</li>
{{ form.as_ul }}
{% endfor %}
</ul>
<input type="submit" value="{{ submit_value|default:"Submit" }}" />
</form>
And my view:
#ownership_required(message="You must be an owner to edit memberships.")
def edit_memberships(request, pk):
project = get_object_or_404(models.Project, pk=pk)
if request.method == "GET":
formset = forms.EditMembershipSet(
queryset=project.membership_set.all()
)
return render_formset_fullpage(request, formset, "Edit Memberships",
template=forms.EDITMEMBERSHIPS_PATH)
elif request.method == "POST":
formset = forms.EditMembershipSet(request.POST)
return HttpResponse("Hooray!")
And the model in question:
class Membership(models.Model):
class Meta:
unique_together = ('project', 'user')
project = models.ForeignKey('common.Project', editable=False)
user = models.ForeignKey('auth.User')
is_owner = models.BooleanField(default=False, blank=False,
help_text="Indicates ownership of the project")
project_alias = models.CharField(max_length=CHARFIELD_SHORT,
help_text="User-given name", blank=True)
The problem is I keep getting a ValidationError back after doing the POST:
ValidationError at /project/4/memberships
[u'ManagementForm data is missing or has been tampered with']
From what I can tell, forms.EditMembership is doing OK. I can render that form and recreate it from the POST with no validation error, so I guess it is something wrong with the way I am using BaseEditMemberships/EditMembershipSet.
EDIT:
Just to give you even more information, here are an example form which is generated and the POST vars for one of my test requests. The form:
<form method="post" action="">
<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='d6d535efd35e83076e1ac14a82ef4cdc' /></div>
<ul>
<li>someuser</li>
<li><label for="id_form-0-is_owner">Is owner:</label>
<input checked="checked" type="checkbox" name="form-0-is_owner" id="id_form-0-is_owner" />
</li>
<li><label for="id_form-0-delete">Delete:</label>
<input type="checkbox" name="form-0-delete" id="id_form-0-delete" />
<input type="hidden" name="form-0-id" value="8" id="id_form-0-id" />
</li>
</ul>
<input type="submit" value="Submit" />
</form>
Corresponding POST vars if I check the delete box:
form-0-id: u'8'
csrfmiddlewaretoken: u'd6d535efd35e83076e1ac14a82ef4cdc'
form-0-delete: u'on'
form-0-is_owner: u'on'
You need to include {{ formset.management_form }} in your template. See Django's formset documentation on rendering templates for formsets.

Categories