I've been going over the docs and some StackOverflow examples but I'm still having a little trouble. In Django I created a form, the view renders it and I have it displayed as form.as_p in the HTML template. This works perfectly, but I would like to be able to customize my HTML template instead of having it displayed as form.as_p. Any examples on how to do so?
So far I have the following.
View:
#login_required
def register(request):
info = Content.objects.get(id=request.user.id)
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES, instance=info)
if form.is_valid():
info = form.save(commit=False)
info.save()
return HttpResponseRedirect('/portal/register')
else:
form = UploadFileForm(instance=info)
return render(request, 'portal/register.html', {'form': form, 'gallery': info})
Form:
class UploadFileForm(ModelForm):
logo = forms.ImageField(required=False)
image1 = forms.ImageField(required=False)
terms = forms.CharField(required=False)
class Meta:
model = Content
fields = ['user', 'logo', 'image1', 'terms']
Model:
class Content(models.Model):
user = models.ForeignKey(User)
logo = models.ImageField(upload_to=content_file_name, null=True, blank=True)
image1 = models.ImageField(upload_to=content_file_name, null=True, blank=True)
terms = models.CharField(max_length="256", blank=True)
def __unicode__(self):
return self.title
HTML Template:
<form method="POST" action="" enctype="multipart/form-data">
{% csrf_token %}
<div class="col-lg-3 col-md-4 col-xs-6 thumb">
<a class="thumbnail" href="#">
{% if gallery.logo %}
<img class="img-responsive" id="logo" src="/media/{{ gallery.logo }}" alt="">
{% else %}
<img class="img-responsive" id="logo" src="/media/images/placeholder.png" alt="">
{% endif %}
</a>
<input type="file" name="logo" id="logo" multiple>
</div>
<div class="col-lg-3 col-md-4 col-xs-6 thumb">
<a class="thumbnail" href="#">
{% if gallery.image1 %}
<img class="img-responsive" id="image1" src="/media/{{ gallery.image1 }}" alt="">
{% else %}
<img class="img-responsive" id="image1" src="/media/images/placeholder.png" alt="">
{% endif %}
</a>
<input type="file" name="image1" id="image1" multiple>
</div>
<div class="form-group">
<input type="text" name="terms" id="terms" class="form-control input-sm" placeholder="terms" value="{{ gallery.terms }}">
</div>
<input type="submit" value="Submit" />
</form>
Yes you can loop on the form fields by doing the following
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
<label>
{{ field.label_tag }}
</label>
<div>
{{ field }}
</div>
</div>
{% endfor %}
Then you can add class to the div and label tags and style the form fields
You can also use Django Widget Tweaks to add classed to the form fields.
Click here to read in more details
By using widget tweaks you simply find the field in which you want to add the class by doing this
{% load widget_tweaks %}
{{ form.name|add_class:"inputContact volunteer" }}
You can render the fields manually:
We don’t have to let Django unpack the form’s fields; we can do it
manually if we like (allowing us to reorder the fields, for example).
Each field is available as an attribute of the form using {{
form.name_of_field }}, and in a Django template, will be rendered
appropriately.
Example from the documentation:
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.subject.errors }}
<label for="{{ form.subject.id_for_label }}">Email subject:</label>
{{ form.subject }}
</div>
<div class="fieldWrapper">
{{ form.message.errors }}
<label for="{{ form.message.id_for_label }}">Your message:</label>
{{ form.message }}
</div>
As I said in the comments, see also Django Crispy Forms. You can achieve the same without so much markup.
Related
Under views.py
def addcomments(request):
comment_text = request.POST.get('comment')
user_id = request.POST.get('user_id')
name = request.POST.get('name')
email = request.POST.get('email')
comment = Comment.objects.create(user_id=user_id, body=comment_text, name=name, email=email)
comment.save()
return HttpResponseRedirect(reverse('users:detail', args=(user_id, )))
this one, from detail.html
{% extends 'base.html' %}
{% block title %}
{{ user.user_fname }} {{ user.user_lname }}
{% endblock %}
{% block content %}
{% if error_message %}
<p class="alert alert-danger">
<strong>{{error_message}}</strong>
</p>
{% endif %}
<div class="row">
<div class="col-lg-6">
<div class="mb-5">
<h1>{{ user.user_fname }} {{ user.user_lname }}</h1>
<p class="text-muted">{{ user.user_email }}</p>
<p>Position: {{ user.user_position }}</p>
</div>
<div class="img-responsive">
<img src="/users/media/{{user.user_image}}" alt="profile_user" class="img-rounded" width="300">
<!-- ito yung hinahanap ng search engine-->
</div>
<div class="btn-group mt-5">
Delete
Edit
Back
</div>
</div>
<div class="col-lg-6">
<h2>Comment</h2>
<p class="text-muted"> Number of comment : {{ comments_count }}</p>
{% if comments_count > 0 %}
{% for comment in comments %}
{% if comment.active %}
<p><strong>{{comment.name}}</strong> : {{comment.body}}</p>
{% endif %}
{% endfor %}
{% endif %}
<hr>
<br><br><br><br><br><br>
<form action="{%url 'users:addcomments'%}" method="post">
{% csrf_token %}
<div class="form-group">
<label>Comment</label>
<textarea name="comment" id="comment" cols="30" rows="5" class="form-control" required placeholder="Enter your comment here ..."></textarea>
</div>
<br>
<input type="text" name="name" id="name" value="{{ user.user_lname }}">
<input type="text" name="lname" id="lname" value="{{ user.user_fname }}">
<input type="text" name="email" id="email" value="{{ user.user_email }}">
<br><br><br><br>
<button type="submit" class="btn btn-sm btn-primary">Add Comment</button>
</form>
</div>
</div>
{% endblock %}
this one for url.py
path('addcomments', views.addcomments, name='addcomments'),
I am having the error message,
IntegrityError at /users/addcomments
(1048, "Column 'user_id' cannot be null")
During handling of the above exception ((1048, "Column 'user_id' cannot be null")), another exception occurred:
from 404
users\views.py, line 146, in addcomments
comment = Comment.objects.create(user_id=users_id, body=comment_text, name=name, email=email) …
Local vars
Variable Value
comment_text
'wwerwr'
email
'HeavyRain#gmail.com'
name
'Heavy'
request
<WSGIRequest: POST '/users/addcomments'>
user_id
None
the heavyrain details, ignore it, was trying stuffs
this one for detail detail is working fine properly, detail is on left side of the page, while addcomments is on the right side of the same page
#login_required(login_url='/users/login')
def detail(request, profile_id):
try:
user = User.objects.get(pk=profile_id)
comments = Comment.objects.filter(user_id=profile_id)
comments_count = Comment.objects.filter(user_id=profile_id).count()
except User.DoesNotExist:
raise Http404("Profile does not exist")
return render(request, 'UI/detail.html', {'user': user, 'comments': comments, 'comments_count': comments_count})
You don't have an user_id field in your html form that's why it becomes None or null and since you don't allow your user_id to be none (don't allow it to be none) django fails to create it. I suggest you get rid of the user_id in the creation and search for a user with that exact unique username that posted the form with
user = request.user
comment = Comment.objects.create(user=user, body=comment_text, name=name, email=email)
I'm sorry but use a model form. It's the MOST appropriate solution. It will manage rendering your form in html and ensuring data is clean and validated.
from django import forms
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = [...]
Please see further information in the documentation.
https://docs.djangoproject.com/en/4.1/topics/forms/modelforms/
I have a contact page with a simple form.
Here is views.py:
def contact_view(request):
if request.method == 'GET':
form = ContactForm()
else:
form = ContactForm(request.POST)
if form.is_valid():
subject = form.cleaned_data['subject']
from_email = form.cleaned_data['from_email']
message = form.cleaned_data['message']
try:
send_mail(subject, message, from_email, settings.ADMIN_EMAILS)
except BadHeaderError:
return HttpResponse('Invalid header found.')
return redirect('success')
return render(request, "base/contact.html", {'form': form})
def success_view(request):
return HttpResponse('Success! Thank you for your message.')
this is contact.html:
{% block content%}
<main class="page contact-page">
<section class="portfolio-block contact">
<div class="container">
<div class="heading">
<h2>Contact me</h2>
</div>
<form method="post">
{% csrf_token %}
<div class="mb-3"><label class="form-label" for="name">Your Name</label><input class="form-control item" type="text" id="name"></div>
<div class="mb-3"><label class="form-label" for="subject">Subject</label><input class="form-control item" type="text" id="subject"></div>
<div class="mb-3"><label class="form-label" for="email">Email</label><input class="form-control item" type="email" id="email"></div>
<div class="mb-3"><label class="form-label" for="message">Message</label><textarea class="form-control item" id="message"></textarea></div>
<div class="mb-3"><button class="btn btn-primary btn-lg d-block w-100" type="submit" value="submit">Submit Form</button></div>
</form>
</div>
</section>
</main>
{% endblock %}
When I use form.as_p it works very well but when I use this template it is not working
it only shows in the terminal that a post request was made.
The html looping syntax of form is following, where we have access to specific field, field.label ,non_field_errors as well as particular field errors.
In your case you can use in this way:
contact.html
{% block content%}
<main class="page contact-page">
<section class="portfolio-block contact">
<div class="container">
<div class="heading">
<h2>Contact me</h2>
</div>
<form method="POST" novalidate>
{% csrf_token %}
{% if form.non_field_errors %}
{% for error in form.non_field_errors %}
<div>
{{error}}
</div>
{% endfor %}
{% endif %}
{% for field in form %}
<p>{{field.label_tag}} {{field}}</p>
<br>
{% for error in field.errors %}
<span>{{error}}</span>
{% endfor %}
{% endfor %}
<input type="submit" value="Save">
</form>
</div>
</section>
</main>
{% endblock %}
You can use it as above it will work perfectly with your existing views, as you said it is working with form.as_p.
If you give only form.as_p, it will render form fields in <p> tag of html, you can see through Ctrl+U of view page source,there we cannot have more control over form.
Your question -- How can i use bootstrap's classes in django's form?
Answer - You can set through widget in your form's fileds. for example:
class MyForm(forms.Form):
name=forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}))
In the above way, you can set it to every field.
I have searched some posts about this issue but still not find solution. After clicking the "submit", the page could redirect to another page. But the sql database doesn't show the submitted information.
Thanks in advance for your suggestion.
views.py
#csrf_exempt
def input(request):
if request.method == 'POST':
form = InputForm(request.POST or None, request.FILES or None)
if form.is_valid():
company = form.cleaned_data['company']
region = form.cleaned_data['region']
uom= form.cleaned_data['uom']
start_date= form.cleaned_data['start_date']
end_date= form.cleaned_data['end_date']
add_input=Input.objects.create(company=company,region=region,uom=uom,start_date=start_date,end_date=end_date)
add_input.save()
return redirect('resut')
else:
print(form.errors)
else:
form = InputForm(initial={'company':'coco','uom':'M$'},instance=Input)
return render_to_response('inputform.html',{'form': form})
models.py
class Input(models.Model):
company=models.CharField(max_length=100)
region=models.CharField(max_length=100)
uom=models.CharField(max_length=100)
start_date=models.DateField(auto_now=False, auto_now_add=False)
end_date=models.DateField(auto_now=False, auto_now_add=False)
forms.py
class InputForm(forms.ModelForm):
company=forms.CharField(widget=forms.TextInput, label="Company",error_messages={'required': 'Please enter your name'},required=True)
regionlist = forms.ModelChoiceField(queryset=Dupont.objects.values('region').distinct())
uom=forms.CharField(required=True)
start_date=forms.DateField(widget=DateInput(),required=True)
end_date = forms.DateField(widget=DateInput(),required=True)
error_css_class='error'
required_css_class = 'required'
class Meta:
model = Input
fields = ('company', 'region','uom','start_date','end_date')
widgets = {
'start_date': forms.DateInput(attrs={'class':'datepicker'}),
'end_date': forms.DateInput(attrs={'class':'datepicker'}),
}
html snippet
<form action="{% url 'result' %}" method="post">{% csrf_token %}
<!--company-->
<div class="field">
<p>Company:<input type="text" name="company" value="{{company}}"/>
</div>
<!--region-->
<div class="field" >
<label> Select the Region:
{{ form.regionlist }}
{% for region in form.regionlist.choices %}
<option value="{{ val }}" {% ifequal data.val val %}selected {% endifequal %}></option>
{% endfor %}
</label>
</div>
<!--uome-->
<div class="field">
<p>Unit of Measure:<input type="text" name="uom" value="{{uom}}"/>
</div>
<!--start date-->
<label for="startDate">Start Month:</label>
<input name="start_date" id="startDate" class="date-picker"/>
<!--END date-->
<label for="endDate">End Month:</label>
<input name="end_date" id="endDate" class="date-picker" />
<!--submit-->
<div class="fieldWrapper">
<p><input type="submit" value="Submit" /></p></div>
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-error">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-error">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}
</form>
No, the action point to another view, instead of input view. I don't paste that view because the issue now is this form's data doesn't saved successfully.
Well then thats your problem, when you submit a form it goes to the specified action. If the snippet is the inputform.html as I suspect it is then you just need to delete the action from your form.
<form method="post">
I have two models.
class ArticleCategory(models.Model):
category = models.CharField(max_length=255,blank=False)
class Article(models.Model):
title = models.CharField(max_length=255,blank=False)
body = models.TextField()
pub_date = models.DateTimeField(auto_now_add=True)
category = models.ForeignKey(ArticleCategory,default=1)
Now I have to render a template and save the form for Article model. I have a foreignKey field in my Article Model and because of that I'm not able to save my article form. I want to select a category from dropdown list and save it in my Article model.
How should I code my template for this ?
My views.py function for this is:
def create(request):
if request.POST:
form = ArticleForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponseRedirect('/articles/all/')
else:
form = ArticleForm()
args={}
args.update(csrf(request))
args['categories'] = ArticleCategory.objects.all()
args['form'] = form
return render_to_response('create_article.html', args)
My template create_article.html currently looks like this:
<form role="form" action="/articles/create/" method="POST" enctype="multipart/form-data">{% csrf_token %}
<div class="row">
<div class="form-group col-lg-3">
<label></label>
<p>{{form.title}}</p>
</div>
<div class="form-group col-lg-3">
<label>Category</label>
<p>
<select id="id_category">
{% for category in categories %}
<option value="{{ category }}">{{ category.category }}</option>
{% endfor %}
</select>
</p>
</div>
<div class="clearfix"></div>
<div class="form-group col-lg-12">
<label>Body</label>
{{form.body}}
</div>
<div class="form-group col-lg-12">
<button type="submit" class="btn btn-default">Save Article</button>
</div>
</div>
</form>
You don't need to do this manually. If your ArticleForm is ModelForm and doesn't exclude category field then you can just write {{ form.category }} and get dropdown created by django automatically. It uses ModelChoiceField underneath the hood.
replace
<select id="id_category">
{% for category in categories %}
<option value="{{ category }}">{{ category.category }}</option>
{% endfor %}
</select>
with
{{ form.category }}
I am using wtforms-recaptcha in order to get the Recaptcha to show.
pip install wtforms-recaptcha
I guided myself on this site to make the installation:
https://pypi.python.org/pypi/wtforms-recaptcha
The problem is that the recaptcha code is being echoed into the form. That is, I see the recaptcha's code on the form and not the recaptcha itself:
<script type="text/javascript" src="https://www.google.com/recaptcha/api/challenge?k=6LeCJvUSAAAAAAvqwJEueVdV0wyNLPtX6KWSTdXp"> </script> <noscript> <iframe src="https://www.google.com/recaptcha/api/noscript?k=6LeCJvUSAAAAAAvqwJEueVdV0wyNLPtX6KWSTdXp" height="300" width="500" frameborder="0"></iframe><br> <textarea name="recaptcha_challenge_field" rows="3" cols="40"> </textarea> <input type="hidden" name="recaptcha_response_field" value="manual_challenge"> </noscript>
Form code on form.py:
from wtforms import PasswordField, StringField, validators, widgets
from wtforms.form import Form
from wtfrecaptcha.fields import RecaptchaField
class ContactForm(Form):
"""Enables the user to provide feedback."""
first_name = StringField('First Name', [
validators.DataRequired()
])
last_name = StringField('Last Name', [
validators.DataRequired()
])
captcha = RecaptchaField('Captcha', [], public_key='6LeCJvUSAAAAAAvqwJEueVdV0wyNLPtX6KWSTdXp', private_key='6LeCJvUSAAAAADcUvYyLv8kt9ARiTAluDGqHBumY', secure=True)
Calling the form from within the HTML:
<form method="post">
{% for field in form %}
<div class="form-group{% if field.errors %} has-error has-feedback{% endif %}">
<div class="row">
<div class="col-xs-12 col-md-4">
{{ field.label(class="control-label") }}
</div>
<div class="col-xs-12 col-md-8">
{{ field(class="form-control") }}
</div>
</div>
{% if field.errors %}
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
{% endif %}
{% for error in field.errors %}
<p class="help-block text-danger">
<span class="glyphicon glyphicon-remove"></span>
{{ error }}
</p>
{% endfor %}
</div>
{% endfor %}
<br>
<button type="submit" class="btn btn-primary">{{ title }}</button>
</form>
Route call code:
#app.route('/contact', methods=['GET', 'POST'])
def contact():
"""Display the contact page."""
form = ContactForm(request.form, captcha={'ip_address': request.remote_addr})
if request.method == 'POST' and form.validate():
return "Thank you for contacting us."
return render_template(
...
)
The issue is that WTForms-RECAPTCH does not return a safe string, but instead returns a unicode string. The underlying issue needs to be fixed here (by returning an instance of wtforms.widgets.core.HTMLString or something else that provides an __html__ method).
To work around the problem for now you should simply mark the field as safe in your template:
<div class="col-xs-12 col-md-8">
{{ field(class="form-control") | safe }}
</div>
Or, alternately, only mark the re-captcha field as safe:
<div class="col-xs-12 col-md-8">
{% if field.short_name == "captcha" %}
{{ field(class="form-control") | safe }}
{% else %}
{{ field(class="form-control") }}
{% endif %}
</div>
There is a PR for this issue and this is fixed as of version 0.3.2