HTML, CSS, flask_wtf how to change form field locations - html

I'm working on my first webapp. I need a simple form for the user to submit a few values. I got it working, but I would like the data entry fields to be on a row instead of in a column.
You can see bootstrap used in the snippets below. I don't think that is involved because the fields render in the same locations without using it.
base.html:
{% extends 'bootstrap/base.html' %}
{% block title %}
...
{% endblock %}
{% block navbar %}
...
{% endblock %}
{% block content %}
<div class="container">
...
{# application content needs to be provided in the app_content block #}
{% block app_content %}{% endblock %}
</div>
{% endblock %}
forms.py:
from flask_wtf import FlaskForm
from wtforms import StringField, IntegerField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired, IPAddress, NumberRange, length
class SnmpQueryForm(FlaskForm):
ip = StringField('IP Address', validators=[IPAddress()])
mask = IntegerField('Mask bits',
validators=[DataRequired(),
NumberRange(22, 32)]
)
snmpcmstr = StringField('Community String',
validators=[DataRequired(),
length(max=20)]
)
submit = SubmitField('Submit Query')
template.html:
{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>Enter SNMP Query Information</h1>
<form action="", method="post">
<div class="row">
<div class="col-md-2">
{{ wtf.quick_form(form) }}
</div>
</div>
</form>
<br>
<br>
<div class="container">
...
Here is the result:
I would like the three fields to be on the same line (in the same row). Any suggestions?
Thank you
Just to add the solution I used for posterity:
All I had to do was change Lakindu's POC code to:
<div class="col-md-4">
<div class="form-group">
{{wtf.form_field(form.ip, class="form-control", placeholder="Enter IP") }}
</div>
</div>
for each input field and add:
<div class="col-md-4">
<div class="input submit">
<input type="submit" value="Submit">
</div>
</div>
for the submit button.
Thank you for the quick help.

This is a proof of concept of what you trying to achieve.
You can use col-md-4 in a row and a container class in order to get three input fields to a single row.
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<form action="" , method="post">
<div class="container">
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label>IP address</label>
<input type="text" class="form-control">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Mask bits</label>
<input type="text" class="form-control">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Community string</label>
<input type="text" class="form-control">
</div>
</div>
</div>
</div>
</form>

Related

How do I move a "submit" button to the right of the HTML page?

How do I move a "submit" button to the right of the HTML page? It seems that the last radio option is squished together with the last submit button. I have tried adding break, but it dosent work
{%extends "new_base.html"%}
{%block content%}
<style> a.mylinklist{
display:block;
}
</style>
<div class="wrap size-1 " >
<h2><strong>Question...</strong>click on the correct answer!</h2>
back to home
<div class = "container" style="width:30%;margin:auto">
<img style="width:100%;height:100%%;object-fit: cover;" src="{{url_for("static", filename="images/" + image)}}"></div></h2>
<br>
<div class="bg-white shadow" style="text-align:center">
<ul class="flexblock reasons" style="height:100%">
<h2 >
<p>{{question}}</p>
</h2>
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for msg in messages %}
<p>{{ msg }}</p>
{% endfor %}
{% endif %}
{% endwith %}
<br>
<br>
<form class="longform" method="POST" action="#" style="margin:auto;display:flex">
{%for opt in options%}
<input class="form-control" type="radio" name="answer" value="{{opt}}" style="display:align;flex-grow:1">
{{options[opt]}}
</input>
{%endfor%}
<button class="btn btn-primary" type="submit" method="POST" style="width:25%;text-align:center;">SUBMIT </button>
</form>
</div>
</div>
{%endblock%}
I see you're using Bootstrap so I assume you have access to all classes in the library. You can edit your submit method as follows;
<button class="btn btn-primary float-right" type="submit" method="POST">SUBMIT </button>
I removed the inline CSS, if you want additional styling you may still add it.
If you are using bootstrap, you can use this class float right as
<button class="btn btn-primary float-right" type="submit" method="POST" style="width:25%;text-align:center;">SUBMIT </button>
to know more about bootstrap float classes check here
you can also do it without bootstrap like this
<button class="btn btn-primary" type="submit" method="POST" style="width:25%;text-align:center;float-right">SUBMIT </button>
to know more about css float property here
The html shared by you is having multiple issues such as an unnecessary closing h2 tag, you should also have a look at self-closing tags
if you are still unable to move your button to right, try update replacing your form opening tag with this
<form class="longform" method="POST" action="#" style="margin: auto">

Is there any method to add bootstrap style to Django password input field?

I'm trying to add a registration page to my Django project and I have made the registration form in the forms.py file.
class createUserForm(UserCreationForm):
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
widgets = {
'username' : forms.TextInput(attrs={'class':'form-control form-input','placeholder':'Username...'}),
'email' : forms.EmailInput(attrs={'class':'form-control form-input','placeholder':'Email...'}),
'password1' : forms.PasswordInput(attrs={'class':'form-control form-input','placeholder':'Password'}),
'password2' : forms.PasswordInput(attrs={'class':'form-control form-input','placeholder':'Re-type password'}),
}
HTML
<h4>Sign in</h4>
<div class="form-container">
<form method="POST" action="">
<div class="form-render" style="display: block;">
{% csrf_token %}
<div style="margin-bottom: 1rem;">
{{form.username}}
</div>
<div style="margin-bottom: 1rem;">
{{form.email}}
</div>
<div style="margin-bottom: 1rem;">
{{form.password1}}
</div>
<div style="margin-bottom: 1rem;">
{{form.password2}}
</div>
<button type="submit" class="btn btn-primary" style="border-radius: 0px; outline-color: #167896; background-color:#167896; width: 15rem; margin-left: 1.5rem;">Submit</button>
</div>
</form>
<div style="display: flex; margin-left: 1.75rem; margin-top: 1rem; margin-bottom: 0rem;">
<p>Already have an account?</p><a style="text-decoration: none; " class="forgot" href="{% url 'login' %}">&nbsp Log in</a>
</div>
</div>
After rendering out HTML, bootstrap styles are not added to the password input fields. This is the screenshot of my output page.
You don't have to send the bootstrap classes as attrs. Just use Crispy Forms
pip install django-crispy-forms
settings.py
INSTALLED_APPS = [
...
"crispy_forms",
]
CRISPY_TEMPLATE_PACK = "bootstrap4"
HTML:
{% load crispy_forms_tags %}
<form method="post">
{{ my_formset|crispy }}
</form>
But does the class appear in the source code? if it is showing, then your bootstrap file is not loading or you are using the wrong measurements
Anyway you can do a test, create something like this:
<style>.form-a{width:100%:!important; height:36px; border:green;}</style>
Put the code above on your page! Now place the form-a class in your <input> and see if it renders with a green border and takes up all the available space, otherwise it means that the classes are not in the rendered source code (client side) and, therefore, there is another problem with your non-bootstrap-related code. If it's like I said, your bootstrap file is not loading or you are using the wrong classes for what you intend to do.
There are several ways to skin this cat, but my preferred method is to use the django-bootstrap4 package: https://pypi.org/project/django-bootstrap4/
Install it in your venv: pip install django-bootstrap4
Add it to your INSTALLED_APPS:
INSTALLED_APPS = (
# ...
"bootstrap4",
# ...
)
Use it in a template:
{% load bootstrap4 %}
<h4>Sign in</h4>
<div class="form-container">
<form method="POST" action="">
{% csrf_token %}
{% bootstrap_form form %}
<button type="submit" class="btn btn-primary">Submit</button>
</div>
It will automatically take care of displaying errors properly. If you need to do any custom actions for fields, you can also loop through the fields:
{% load bootstrap4 %}
<h4>Sign in</h4>
<div class="form-container">
<form method="POST" action="">
{% csrf_token %}
{% for field in form %}
{% bootstrap_field field %}
{% endfor %}
<button type="submit" class="btn btn-primary">Submit</button>
</div>
Good luck!

Django. Reply for comments getting posted as comments. How to have the reply for comments?

Hey I am quite new to django, I have this function for comments and replies,the problem is I can't have the reply to comments instead it is getting posted as comments. How to have this replies under respective comments? Here is my model and functions.
model
class Comment(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
reply = models.ForeignKey('Comment', null=True, related_name='replies', blank=True, on_delete=models.CASCADE)
content = models.TextField(max_length=1000)
timestamp = models.DateTimeField(auto_now_add=True)
views
def comment_section(request, slug):
user = request.user
post = get_object_or_404(Post, slug=slug)
comments = Comment.objects.filter(post=post, reply=None).order_by('-id')
if request.POST:
comment_form = CommentForm(request.POST or None)
if comment_form.is_valid():
content = request.POST.get('content')
reply_id = request.POST.get('comment_id') #reply-section
comment_qs = None
if reply_id:
comment_qs = Comment.objects.get(id=reply_id)
comment = Comment.objects.create(post=post, user=request.user, content=content, reply=comment_qs)
comment.save()
else:
comment_form = CommentForm()
context = {
'post':post,
'comment_form':comment_form,
'comments': comments,
}
if request.is_ajax():
html = render_to_string('posts/comment_section.html', context, request=request)
return JsonResponse({'form': html})
html
<!-- Comment showing section -->
<div class="main-comment-section">
<div class="container-fluid mt-2">
<div class="form-group row">
<!-- Comment Form -->
<form class="comment-form" method="post" action="{% url 'posts:comment_section' post.slug %}">
{% csrf_token %}
<div class="form-group">
<textarea name="content" cols="60" rows="2" maxlength="1000" required="" id="id_content"></textarea>
</div>
<button type="submit" value="submit" class="btn-sm btn-outline-warning" style="color: black;">Comment</button>
</form>
<!-- Comment Form end -->
</div>
</div>
{% if not post.comment_set.all %}
<small>No comments to display</small>
{% endif %}
{% for comment in post.comment_set.all %}
<blockquote class="blockquote">
<img style="float:left; clear: left;" class="rounded-circle article-img" height="10" width="10" src="{{ comment.user.profile.profile_pic.url }}"><h6>{{ comment.user.first_name|capfirst }} {{ comment.user.last_name|capfirst }}</h6><br>
<p style="font-size: 8px;">{{ comment.timestamp }}</p>
<p style="font-size: 14px;" class="mb-3">{{ comment.content }}</p>
<a type="button" name="button" class="reply-btn ml-4"><i class="fas fa-reply fa-sm"></i></a> 
{% if request.user == comment.user %}
<i class="fas fa-trash fa-sm" style="color: brown;"></i></td>
{% endif %}
</blockquote>
{{ comment.reply.count }}
<div class="replied-comments col-md-5" style="display: none;">
{% for reply in comment.replies.all %}
<blockquote class="blockquote">
<img style="float:left; clear: left;" class="rounded-circle article-img" height="50" width="50" src="{{ reply.user.profile.profile_pic.url }}"><h6>{{ reply.user.first_name|capfirst }} {{ reply.user.last_name|capfirst }}</h6><br>
<p style="font-size: 13px;" class="mb-3">{{ reply.content }}</p>
</blockquote>
{% endfor %}
<div class="form-group row">
<form class="reply-form" method="post" action="{% url 'posts:comment_section' post.slug %}">{% csrf_token %}
<input type="hidden" name="comment_id" value="{{ comment.id }}">
<div class="form-group">
<textarea name="content" cols="60" rows="2" maxlength="1000" required="" id="id_content"></textarea>
</div>
<input type="submit" value="submit" class="btn-sm btn-outline-light" style="color: black;">
</form>
</div>
</div>
{% endfor %}
</div>
I guess there is some problem with the form submission, but I can't figure out what it is.
Thanks
This is how I easily solve the problem and it helps me in many ways.
comments = Comment.objects.filter(post=post, reply=None).order_by('-id')
{% for comment in comments %}
{% if comment.reply %}
The IF statement will check if the comment is a reply, this have a parent
If it is, you can access it with {{comment.reply.content}} and {{comment.reply.timestamp}} to print the parent. And {{comment.content}} to print the reply.
{% else %}
I.e. if the comment is just a comment and not a reply.
Just print the comment
{{comment.content}}
{% endfor %}
It is because replies are also comments. You are loading comments in views correctly by :
comments = Comment.objects.filter(post=post, reply=None).order_by('-id')
but in the template you are loading both comments and replies in :
{% for comment in post.comment_set.all %}
try :
{% for comment in comments %}
to load post comments and select replies of each comment by :
{% for reply in comment.replies.all %}

How to visualize padding and margins in django templates and reducing white space between rows in bootstrap

I'm new to html and Django, and Bootstrap. I sometimes have trouble understanding the padding and margins. Is there some kind of clever way to use CSS to show the padding and margins visually. From what I can tell margins don't have a background color and are transparent. For example in the code below I would like to reduce the vertical space between the fields so the form would be more compact. If I just use HTML I don't seem to have a problem, but when I am using widget_tweaks I get additional space between the rows.
{% load widget_tweaks %}
{% block content%}
<div class="containter p-4">
<div class="py-0 text-Left">
<div>
<h2>Just a title for the page:</h2>
</div>
<form action="{% url 'addINNO:add_new' %}" method="POST">
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
<div class="row py-0 ">
<div class="form-group py-0 col-9">
<div class="form-group py-0">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{% if field.id_for_label == 'id_description' %}
{{ field|add_class:'form-control p-0'|attr:"rows:3"}}
{% for error in field.errors %}
<span class="help-block">{{ error }}</span>
{% endfor %}
{% elif field.id_for_label == 'id_comment' %}
{{ field|add_class:'form-control p-0'|attr:"rows:2"}}
{% for error in field.errors %}
<span class="help-block">{{ error }}</span>
{% endfor %}
{% else%}
{{ field|add_class:'form-control p-0' }}
{% for error in field.errors %}
<span class="help-block">{{ error }}</span>
{% endfor %}
{% endif %}
</div>
</div>
</div>
{% endfor %}
<input type="submit" value="Save and Return to Home Page">
</form>
</div>
</div>
{% endblock %}
When I right click and inspect elements, I don't see any margins that are not 0px and padding also seems to be 0 px.
So what am I missing? How can I reduce the white space between rows?
Chrome Dev tools are your best friend, hit F12 and you'll see all your elements, select the element you want to see and you can even change padding live to see what it will look like. Firefox also has great css dev tools

How to display user- defined number of fields of a form?

I have a created a form using HTML and Jinja2 to configure a CSV file. The user is asked to change the name and assign a type to each column name. The CSV file can have any number of columns, therefore, I have created the form through looping Currently, I use the bootstrap grid and divide into three columns and rows depending on the number of columns.
<form action="/updateFile" method="POST" enctype="multipart/form-data">
<div class="row">
<div class="col-sm-12">
<p class="form-group">
{% for x in title %}
{% if loop.index == 1 %} <div class="row"> {% endif %}
<div class="col-xs-12 col-sm-12 col-lg-4 col-md-4">
<div class="row">
<div class="col-sm-6">
<label> Field {{ loop.index }} : {{ x }} </label>
<input type="text" title="Name should only contain letters or numbers" class="form-control"
name="field-{{ loop.index }}" value="{{ x }}" maxlength="10" pattern="[a-zA-Z0-9_ ]+" required>
</div>
<div class="col-sm-6 pull-right">
<label> </label>
<select class="form-control " name="choice-{{ loop.index }}">
<option value="mcqsr">MCQ-Single Response</option>
<option value="mcqmr">MCQ-Multiple Response</option>
<option value="num">Numerical</option>
<option value="tim">Temporal</option>
<option value="lat">Survey Site - Latitude</option>
<option value="lng">Survey Site - Longitude</option>
<option value="temp">Survey Site - Temporal</option>
</select>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<label> </label>
<input type="text" title="Name should only contain letters or numbers" class="form-control"
name="question-{{ loop.index }}" value=" Question of {{ x }}" pattern="[a-zA-Z0-9_ ]+" required>
</div>
</div>
</div>
{% if loop.index % 3 == 0 %} </div><p> </p><p> </p><div class="row"> {% endif %}
<input type="hidden" name="title-{{ loop.index }}" value= {{x}}>
{% endfor %}
</div>
</div>
<div class="row">
<div class="col-sm-12 text-center">
<input type="hidden" name="filename" value= {{filename}}>
<input type="hidden" name="numofField" value= {{numofField}}>
<button class="btn btn-primary" type="submit" value="Update">Submit</button>
</div>
</div>
</div>
</form>
I would like to include an option the user to choose the number of variables he/she wants to choose. Similar to the e-commerce sites or photo albums, they generally give you an option to "show 25" at a time, and give you the option of changing that to "show 50", "show 100", or "show all" in order to allow the user have the control in the amount of clutter they will be able to navigate through in the page. I could find few examples like datatables.js but they only work on tables. I could not find a way how to go about it?
If you access to javascript you can write logic something like this.
let container = document.getElementById('container');
for (let i=0; i<200; i++) {
let div = document.createElement('div');
div.className = 'box';
container.appendChild(div);
}
var items = document.getElementsByClassName('box');
function show(howMany) {
showAll();
let count = parseInt(howMany);
for(let i = 0; i<items.length;i++){
if(i<count){
continue
} else {
items[i].style.display = 'none';
}
}
}
function showAll() {
for(let i = 0; i<items.length;i++){
items[i].style.display = 'inline-block';
}
}
.box{
display: inline-block;
height: 25px;
width: 25px;
background: tomato;
margin-right: 4px;
}
<button onclick="show('50')">show 50</button>
<button onclick="show('100')">show 100</button>
<button onclick="showAll()">show all</button>
<div id="container"></div>