custom django form template (in bootstrap) - python

I would like to customize my django forms.
For example, in my code I have working hour settings which I like to produce like this format:
mon_start mon_end
tue_start tue_end
....
but instead it creates a form
mon_start
mon_end
tue_start
tue_end
....
Here is a view of the output that I dont want
Below are my code:
forms.py
class CompanyWorkingHoursSettingsForm(forms.ModelForm):
mon_start = forms.TimeField()
mon_end = forms.TimeField()
tue_start = forms.TimeField()
tue_end = forms.TimeField()
class Meta:
model = Workinghours
fields = ("mon_start","mon_end","tue_start","tue_end")
workinghourssettings.html
{% extends 'project/base.html' %}
{% load bootstrap3 %}
{% block page %}
<div class="col-lg-12">
<div class="panel">
<div class="panel-heading bg-blue">
<h4 class="panel-title text-center text-white">
Working Hours Settings
</h4>
</div>
<div class="panel-body">
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_form company_workinghourssettings_form %}
<button type="submit" class="btn btn-pink">Update</button>
</form>
</div>
</div>
</div>
{% endblock %}
How do i produce a custom arranged form for my form fields above ? (in bootstrap)

As suggested in the comments you may try rendering your fields one by one, and apply bootstrap grid:
<form>
<div class="row">
<div class="col-md-6">{{ my_form.field_1 }}</div>
<div class="col-md-6"{{ my_form.field_2 }}></div>
</div>
....
</form>

If the problem is with the placeholder you can just use
mon_start =forms.TimeField(label='the label', widget=forms.TextInput(attrs={'placeholder': 'What ever you want here'}))

Related

improperly configured at /18/delete, Django views issue

I have searched through the other questions similar to my own problem and have come to no solution so im hoping someone can help me figure out where i went wrong.
I'm trying to implement a delete post option in my blog program but it is throwing the following error once you click the 'delete' button:
ImproperlyConfigured at /18/delete/
Deletepost is missing a QuerySet. Define Deletepost.model, Deletepost.queryset, or override Deletepost.get_queryset().
I am nearly sure its a problem with my URLS.py though what exactly i cannot figure out.
the following is the code in question:
Views.py
# delete post
class Deletepost(LoginRequiredMixin, DeleteView):
form_class = Post
success_url = reverse_lazy('blog:home')
template_name = 'templates/post.html'
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
return False
urls.py
urlpatterns = [
# home
path('', views.postslist.as_view(), name='home'),
# add post
path('blog_post/', views.PostCreateView.as_view(), name='blog_post'),
# posts/comments
path('<slug:slug>/', views.postdetail.as_view(), name='post_detail'),
# edit post
path('<slug:slug>/edit/', views.Editpost.as_view(), name='edit_post'),
# delete post
path('<int:pk>/delete/', views.Deletepost.as_view(), name='delete_post'),
# likes
path('like/<slug:slug>', views.PostLike.as_view(), name='post_like'),
]
post.html
{% extends 'base.html' %} {% block content %}
{% load crispy_forms_tags %}
<div class="masthead">
<div class="container">
<div class="row g-0">
<div class="col-md-6 masthead-text">
<!-- Post title goes in these h1 tags -->
<h1 class="post-title text-success">{{ post.title }}</h1>
<!-- Post author goes before the | the post's created date goes after -->
<p class="post-subtitle text-success">{{ post.author }} | {{ post.created_on }}</p>
</div>
<div class="d-none d-md-block col-md-6 masthead-image">
<!-- The featured image URL goes in the src attribute -->
{% if "placeholder" in post.featured_image.url %}
<img src="https://codeinstitute.s3.amazonaws.com/fullstack/blog/default.jpg" width="100%">
{% else %}
<img src=" {{ post.featured_image.url }}" width="100%">
{% endif %}
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col card mb-4 mt-3 left top">
<div class="card-body text-dark">
<!-- The post content goes inside the card-text. -->
<!-- Use the | safe filter inside the template tags -->
<p class="card-text text-dark">
{{ post.content | safe }}
</p>
<div class="row">
<div class="col-1">
<strong>
{% if user.is_authenticated %}
<form class="d-inline" action="{% url 'post_like' post.slug %}" method="POST">
{% csrf_token %}
{% if liked %}
<button type="submit" name="blogpost_id" value="{{post.slug}}" class="btn-like"><i class="fas fa-heart"></i></button>
{% else %}
<button type="submit" name="blogpost_id" value="{{post.slug}}" class="btn-like"><i class="far fa-heart"></i></button>
{% endif %}
</form>
{% else %}
<span class="text-secondary"><i class="far fa-heart"></i></span>
{% endif %}
<!-- The number of likes goes before the closing strong tag -->
<span class="text-secondary">{{ post.number_of_likes }} </span>
</strong>
</div>
<div class="col-1">
{% with comments.count as total_comments %}
<strong class="text-dark"><i class="far fa-comments"></i>
<!-- Our total_comments variable goes before the closing strong tag -->
{{ total_comments }}</strong>
{% endwith %}
</div>
<div class="col-1">
<a class="btn btn-outline-danger" href="{% url 'delete_post' post.id %}">Delete It</a>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col">
<hr>
</div>
</div>
<div class="row">
<div class="col-md-8 card mb-4 mt-3 ">
<h3 class="text-dark">Comments:</h3>
<div class="card-body">
<!-- We want a for loop inside the empty control tags to iterate through each comment in comments -->
{% for comment in comments %}
<div class="comments text-dark" style="padding: 10px;">
<p class="font-weight-bold">
<!-- The commenter's name goes here. Check the model if you're not sure what that is -->
{{ comment.name }}
<span class=" text-muted font-weight-normal">
<!-- The comment's created date goes here -->
{{ comment.created_on }}
</span> wrote:
</p>
<!-- The body of the comment goes before the | -->
{{ comment.body | linebreaks }}
</div>
<!-- Our for loop ends here -->
{% endfor %}
</div>
</div>
<div class="col-md-4 card mb-4 mt-3 ">
<div class="card-body">
<!-- For later -->
{% if commented %}
<div class="alert alert-success" role="alert">
Your comment is awaiting approval
</div>
{% else %}
{% if user.is_authenticated %}
<h3 class="text-dark">Leave a comment:</h3>
<p class="text-dark">Posting as: {{ user.username }}</p>
<form class="text-dark" method="post" style="margin-top: 1.3em;">
{{ comment_form | crispy }}
{% csrf_token %}
<button type="submit" class="btn btn-signup btn-lg">Submit</button>
</form>
{% endif %}
{% endif %}
</div>
</div>
</div>
</div>
{% endblock content %}
Any ideas?
I think it should be model not form_class so:
class Deletepost(LoginRequiredMixin, DeleteView):
model = Post
success_url = reverse_lazy('blog:home')
template_name = 'templates/post.html'
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
return False
#SunderamDubey's answer is correct. The test_func will however not run, since this is method of the UserPassesTestMixin [Django-doc], not LoginRequiredMixin.
But using a test function as is done here is not efficient: it will fetch the same object twice from the database. You can simply restrict the queryset, like:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from django.views.generic import DeleteView
class DeletePostView(LoginRequiredMixin, DeleteView):
model = Post
success_url = reverse_lazy('blog:home')
template_name = 'templates/post.html'
def get_queryset(self, *args, **kwargs):
return (
super().get_queryset(*args, **kwargs).filter(author=self.request.user)
)
This will do filtering at the database side, and thus return a 404 in case the logged in user is not the author of the Post object you want to delete.
In the template, you will need to make a mini-form to make a POST request, for example:
<form method="post" action="{% url 'delete_post' post.id %}">
{% csrf_token %}
<button class="btn btn-outline-danger" type="submit">Delete</button>
</form>
In my opinion, you should change url to below
'path('delete/int:pk', views.Deletepost.as_view(), name='delete_post'),'
if didn't work you can do this
def delete_post(request, post_id):
post = Post.objects.get(pk=post_id)
post.delete()
return redirect('blog:home')

rendering forms into HMTL using django-easy-select2

I am using django-easy-select2 to handle the entering of data into several manytomanyfields within a model - titled Engagement.
I am using bootstrap and crispy forms to render the Engagement form to HTML.
The rendering is broadly working as expected/required. However, the size of form fields for manytomany data are initially very small and require data to the selected/entered, before they expand. Once data is entered the fields do expand. But, I would like these fields to initially render as the size set by bootstrap.
For example, I've set bootstrap as col-6, but the initial render of the manytomany is only col-1 or even less. When data is entered that field will expand up to col-6 and no further, which good, but I would like the field to start at col-6, even with no data.
Relevant code is below.
engagements.view:
class EngagementCreateView(CreateView):
model = Engagement
form_class = select2_modelform(Engagement, attrs={'width': 'auto'}) # this sets the widths of the field
template_name = "engagements/engagement_create.html"
def form_valid(self, form):
print(form.cleaned_data)
return super().form_valid(form)
create engagement template
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<h4>{% if form.instance.pk %}Edit engagement{% else %}Create new engagement{% endif%}</h4>
<div class="form-group">
<form method="post" novalidate>
{% csrf_token %}
<div class="row mt-1">
<div class="col-6"> # I'm rendering the width of fields here
{{ form.date|as_crispy_field }}
{{ form.projects|as_crispy_field }}
{{ form.stakeholders|as_crispy_field }}
{{ form.ppdds|as_crispy_field }}
</div>
<div class="col-6">
{{ form.follow_up_date|as_crispy_field }}
{{ form.engagement_types|as_crispy_field }}
{{ form.engagement_workstreams|as_crispy_field }}
</div>
</div>
<div class="row mt-1">
<div class="col-lg">
{{ form.summary|as_crispy_field }}
</div>
</div>
<br>
<input class="btn btn-primary" type="submit"
value="{% if form.instance.pk %}Save{% else %}Create{% endif%}">
<a class="btn btn-primary" href="../">Cancel</a></p>
</form>
</div>
{% endblock %}
I think the most relevant part of the django-easy_select2 documentation is here https://django-easy-select2.readthedocs.io/en/latest/usage.html.

How can I raise ValidationError using DjangoForm?

I am quite new to programming and I am trying to build my first webiste!
I am trying to validate some fields of a form on server side and I cannot understand why it is not displayed to the user on HTML page. I have tried the different approaches, but unsuccessfully. I know it is quite newbie question, but I cannot understand why it is not working.
forms.py
class SuggestionForm(forms.Form):
name = forms.CharField(max_length=30)
last_name = forms.CharField(max_length=30)
text = forms.CharField(max_length=800, widget=forms.Textarea)
def clean_text(self):
data = self.cleaned_data['text']
print(data)
if len(data) < 50:
raise forms.ValidationError('The text cannot be smaller than 50 char')
return data
views.py
def suggestion_add(request):
if request.method == "POST":
form = SuggestionForm(request.POST)
if form.is_valid():
suggestion = Suggestions.objects.create(
name = form.cleaned_data['name'],
last_name = form.cleaned_data['last_name'],
text = form.cleaned_data['text']
)
suggestion.save()
return redirect("success")
else:
form = SuggestionForm()
form.fields['name'].widget.attrs.update({
'placeholder': 'Your name'
})
form.fields['last_name'].widget.attrs.update({
'placeholder': 'Your surname'
})
form.fields['text'].widget.attrs.update({
'placeholder': 'Your thoughts'
})
return render(request, "suggestions_add.html", {'form':form})
html
<form action="" enctype="multipart/form-data" method="post">
<div id='id_name' class="control-group">
<label for="id_name" class="control-label">
Name
</label>
<div class="controls">
{{ form.name }}
</div>
</div>
<div id='id_prenume' class="control-group">
<label for="id_prenume" class="control-label">
Last name
</label>
<div class="controls">
{{ form.last_name }}
</div>
</div>
<div id='id_text' class="control-group">
<label for="id_text" class="control-label">
Text
</label>
<div class="controls">
<label for="field"></label>
{{ form.text }}
</div>
<div class="characters">
<span id="charNum"> </span> <span id="charText"></span>
</div>
</div>
<div class="wrap-send">
<input type="submit" value="Send" class="drpbtn" id="send-sugestion">
</div>
{% csrf_token %}
</form>
I tried every approach I found on internet, and definitely something's up with my code because I cannot understand why it is not showing the error to the user!
Thank you so much for your time!
Because you do not render any errors. In order to render any errors, you should render not only the field but the errors as well. This is discussed in the rendering fields manually section of the documentation. For example for your text field you can render the errors with:
{% if form.text.errors %}
<ol>
{% for error in form.text.errors %}
<li><strong>{{ error|escape }}</strong></li>
{% endfor %}
</ol>
{% endif %}
<div class="controls">
<label for="field"></label>
{{ form.text }}
</div>
You can of course add a different styling, etc.
You should do this for all form fields, since each field can raise errors.
Furthermore there can also be non-field errors: these are errors not related to a specific field, but often a combination of fields. These are typically rendered at the top of the form with something like:
{% if form.non_field_errors %}
<ol>
{% for error in form.non_field_errors %}
<li><strong>{{ error|escape }}</strong></li>
{% endfor %}
</ol>
{% endif %}

Django Forms not showing

I am trying to use Django provided forms instead of manually creating them with HTML. When I do this, however, they do not appear. The questions/answers I have found on this site have so far been unable to solve my issue. (unless I am reading them wrong.)
forms.py
from django import forms
class KeywordForm(forms.Form):
input_keywords = forms.CharField(label="Keywords", max_length='100')
class LocationForm(forms.Form):
input_location = forms.CharField(label="Location", max_length="250")
views.py
from django.shortcuts import render
from .forms import KeywordForm, LocationForm
def search_view(request):
keyword_form = KeywordForm()
location_form = LocationForm()
return render(request, 'search_results.html', {'keyword_form': keyword_form, 'location_form': location_form})
urls.py
from django.urls import path
from . import views
urlpatterns = [
...
path('search/', views.search_view, name='search'),
]
base.html
{% block search %}
<div class="card">
<div class="card-body">
<form class="row align-items-center">
<form action="search" method="GET">
<div class="col-sm-2">
{{ keyword_form }}
</div>
<div class="col-sm-2">
{{ location_form }}
</div>
<!-- <label for="inputKeywords" class="form-label">Keywords</label>-->
<!-- <input class="form-control" id="inputKeywords" type="text" name="inputKeywords" placeholder="Enter Keywords...">-->
<!-- </div>-->
<!-- <div class="col-sm-2">-->
<!-- <label for="inputLocation" class="form-label">Location</label>-->
<!-- <input type="Text" class="form-control" id="inputLocation" name="inputLocation" placeholder="Enter location...">-->
<!-- </div>-->
<div class="col">
<input class="btn btn-primary" type="submit" value="Search">
</div>
</form>
</form>
</div>
</div>
{% endblock %}
Per request: search_results.html, which isn't finished due to the forms not showing up in base.html.
{% extends 'base.html' %}
{% block search %}
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="navbar-nav">
<form action="search" method="GET">
{{ keyword_form }}
{{ location_form }}
<input class="btn btn-primary" type="submit" value="Search">
</form>
</div>
</nav>
{% endblock %}
In base.html replace this:
{% block search %}
<div class="card">
<div class="card-body">
<form class="row align-items-center">
<form action="search" method="GET">
<div class="col-sm-2">
{{ keyword_form }}
</div>
<div class="col-sm-2">
{{ location_form }}
</div>
<div class="col">
<input class="btn btn-primary" type="submit" value="Search">
</div>
</form>
</form>
</div>
</div>
{% endblock %}
with this:
{% block search %}
{% endblock search %}
Let me see if i can explain clearly what's happening here..
You created a view for search_results.html so when you render that page the view passes the context variable to it, which includes the forms. All good 'till here.
From the code you shared i see that then you went ahead and added the Django Template Language to render those forms directly in base.html. But that won't work because base.html doesn't have access to the context variable you're passing to search_results.html.
When you extend a template and you add content within blocks that are present in the parent template, what happens is that the block in question is overwritten.
So in your case, whatever you're writing between search blocks in base.html will be wholly overwritten by what's in between those same blocks in search_results.html.
From what i see there are no errors in the code so I think all you have to do is to work on your search_results.html template. And when you test if it works or not do so on search_results.html, not anywhere else, because that's the template that will have access to the context dictionary.
The problem might also be that for some reason those form elements are empty, or that something is wrong at some other step of the way. The template doesn't mind being passed empty context variables so you should also consider that maybe the forms are being passed to the template but they're just empty. You have to run little tests for these things. Try passing a variable containing a simple string to the template and try to render that. Does it work? Good, at least you know that the problem is somewhere else. Doesn't it? Even better, you're one step closer to the solution.

i am getting an error saying category matching query does not exist

The voting proceess is working fine with this code. The problem is only when redirecting after voting the options.
Exception Type:DoesNotExist
Exception Value:
Category matching query does not exist.
category = Category.objects.get(slug=slug)
urls.py
path('<slug>/',views.options,name='options'),
path('<slug>/vote/', views.vote, name='vote'),
views.py
def home(request):
categories = Category.objects.filter(active=True)
return render(request,'rank/base.html',{'categories': categories,'title':'TheRanker'})
def options(request,slug):
category = Category.objects.get(slug=slug)
options = Option.objects.filter(category=category)
return render(request,'rank/options.html',{'options':options,'title':'options'})
def vote(request,slug):
option = Option.objects.get(slug=slug)
if Vote.objects.filter(slug=slug,voter_id=request.user.id).exists():
messages.error(request,'You Already Voted!')
return redirect('rank:options',slug)
else:
option.votes += 1
option.save()
voter = Vote(voter=request.user,option=option)
voter.save()
messages.success(request,'Voted!')
return redirect('rank:options',slug)
options.html
{% extends "rank/base.html" %}
<title>{% block title %}{{title}}{% endblock title%}</title>
{% load bootstrap4 %}
{% block content %}
<center><br>
<center>{% bootstrap_messages %}</center>
<ol type="1">
{% for option in options %}
<div class="col-lg-6 col-md-6 mb-6">
<div class="card h-100">
<div class="card-body">
<b><li>
<img src="/media/{{option.image}}" width="200" height="100">
<h4>{{option.name}}
</h4>
<h5 class="card-text">{{ option.details}}</h5>
<h5>{{ option.votes }} votes</h5>
<form action="{% url 'rank:vote' option.slug %}" method="post">
{% csrf_token %}
<input type="submit" class="btn btn-success" value="Vote" >
</form>
</li></b>
</div>
<div class="card-footer">
<small class="text-muted"></small>
</div>
</div>
</div>
{% endfor %}
</ol>
</center>
{% endblock content%}
You're confusing categories and options. The form sends the slug of the option, but then you redirect to the categories view using the same slug. But those are two different models.

Categories