How to use Django template context processor and form together? - django

I am a newbie to Django. In a project I have to take inputs for multiple models in Django using forms.
For every model I have written functions (in views.py) and its corresponding Django template (in template folder).
Such as,my Add Teacher function is,
def add_teacher(request):
form = TeacherForm()
if request.method=="POST":
form = TeacherForm(request.POST)
if form.is_valid():
form.save(commit=True)
return index(request)
else:
print(form.errors)
return render(request,"billing/add_teacher.html",{"form":form})
And billing/add_teacher.html template is,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Add Teacher</title>
</head>
<body>
<h1>Add a Discipline</h1>
<div>
<form id="teacher_form" method="post" action="/billing/add_teacher/">
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
{{ field.help_text }}
{{ field }}
{% endfor %}
<input type="submit" name="submit" value="Add Teacher"/>
</form>
</div>
</body>
</html>
Now, I want to use a template for all of my functions.Such as, I want to use this template for all functions with the help of Django template context processor.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
</head>
<body>
<h1>{{ h1 }}</h1>
<div>
<form id={{ form_id }} method="post" action="{{ action }}">
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
{{ field.help_text }}
{{ field }}
{% endfor %}
<input type="submit" name="submit" value={{ value }}/>
</form>
</div>
</body>
</html>
But when I return render() function I only can set either context or form.Such as, I can use one of these,
return render(request,"billing/add_teacher.html",{"form":form})
or,
return render(request,"billing/add_teacher.html",context= context_dict)
How can I use both of these together?
Something like,
return render(request,"billing/add_teacher.html",{"form":form},context =context_dict)
Thank you.

Try the following
context_dict.update({"form":form})
return render(request, "billing/add_teacher.html", context=context_dict)

Related

Flask.flash messages not available through extended template

I am having trouble with sending flashed messages to a route that extends its layout from another template. This message shows up just fine if use the message in the layout.html which makes me believe that rendering login.html first will render layout.html and use the flashed message there and not pass it to my /login route. How are you able to call this message in an extended template? I am using the jijna with syntax taken from here to be able to have the message variable available within my mainblock. Flask's documentation does not specify this either.
app.py
#app.route("/login", methods=["POST", "GET"])
def login():
# Forget any previous user
if session.get("user_id"):
session.pop("user_id")
if request.method == "POST":
# Create connection cursor
cursor = mysql.connection.cursor()
# Query database for email
cursor.execute("SELECT id, email, password FROM users WHERE email = %s", [request.form.get("email")])
row = cursor.fetchone()
print(row)
if row is None:
print("WHY")
flash("Invaid user")
return redirect("login")
My layout.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hoook - {% block title %}{% endblock %}</title>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0, width=device-width">
<link href="/static/favicon-16x16.png" rel="icon">
<link href="/static/style.css" rel="stylesheet">
<!-- Scripts -->
<script src="https://kit.fontawesome.com/542c2d099e.js" crossorigin="anonymous"></script>
<script src="/static/mainJS.js"></script>
</head>
<body>
<div class="page-wrapper">
<header>
<nav class="main-navbar">
{% if request.path == "/login" %}
<div class="navbar-container login-container">
{% else %}
<div class="navbar-container">
{% endif %}
<div>
{% if request.path == "/login" %}
<img src="/static/hoook_logo_blue.png" alt="Hoook Logo" height="50" width="150">
{% else %}
<img src="/static/hoook_logo.png" alt="Hoook Logo" height="50" width="150">
{% endif %}
</div>
{% if request.path != "/login" %}
<div>
{% if session["user_id"] %}
{# change nav bar for logged in users #}
{% else %}
{# work on this nav bar for unlogged in users #}
{% if request.path == "/signup" %}
<a class="navbar-link" href="/login">Sign in</a>
{% endif %}
{% endif %}
</div>
{% endif %}
</div>
</nav>
</header>
</div>
<main>
{% if request.path == "/login" %}
<div class="top-container signup-container">
{% else %}
<div class="top-container">
{% endif %}
{% with messages = get_flashed_messages() %}
{% block main %}{% endblock %}
{% endwith %}
</div>
</main>
<footer>
</footer>
</body>
</html>
My login.html
{% extends "layout.html" %}
{% block title %}
Login
{% endblock %}
{% block main %}
<div class="login-div">
<div>
<h1 class="color-control">Sign in to Hoook</h1>
</div>
<div class="login-input-bx">
<form action="/login" method="post" autocomplete="off">
<div class="form-control login-form-control">
<label class="login-label color-control" for="email">Email address</label>
<input class="login-input" type="text" name="email" id="email" required autofocus>
</div>
<div class="form-control login-form-control">
<label class="login-label color-control" for="password">Password</label>
<input class="login-input" type="password" name="password" id="password" required readonly onfocus="this.removeAttribute('readonly')">
</div>
<button class="btn btn-login" type="submit">Sign in</button>
</form>
</div>
{% if messages %}
{% for msg in messages %}
<div class="flashed-messages-div">
<p class="signup-para" id="login-flashed-messages">Error: {{ msg }}</p>
</div>
{% endfor %}
{% endif %}
<div class="signup-link-div">
<p class="color-control signup-login-font">New to Hoook? <a class="signup-link-anchor" href="/signup">Create an account</a>.</p>
</div>
</div>
{% endblock %}
Update
I guess I could do something like make_response instead as seen here. and just use:
response = make_response(render_template("login.html", message = "Invalid user"), 302)
return response
However I am curious if there is a way to pass the flashed message through instead.
I have had the same issue. Instead of:
return redirect("login")
try with:
return render_template("login.html")
The flashed message works that way for me.

Why htmx trigger only work once in django

I am using htmx to trigger a field in Django ModelForm with the following codes.
Everything works as it supposed to the first time around, but after that when you change the option select field nothing happen, no trigger whatsoever. I have to reset and go back to url 'listing' for it to respond again. I want the code to trigger the result everytime I change the option select field before I finally submit. Any help is well appreciated.
class Listing(model.Model):
option=models.ForeignKey(Option,on_delete=models.CASCADE)
package=models.ForeignKey(Package,on_delete=models.CASCADE,blank=True,null=True)
number=models.ForeignKey(Number,on_delete=models.CASCADE,blank=True,null=True)
period=models.ForeignKey(Period,on_delete=models.CASCADE,blank=True,null=True)
title=models.CharField(max_length=20)
class ListingForm(ModelForm):
class Meta:
model=Listing
fields='__all__'
class ListingCreateView(CreateView):
model=Listing
form_class=ListingForm
template_name='listing_form.html'
success_url='/forms/listing/'
def option(request):
option=request.GET.get('option')
form=ListingForm
context={'option':option,'form':form}
return render(request,'partial_option.html',context)
urlpatterns=[
path('',ListingCreateView.as_view(),name='listing-create'),
path('option/',option,name='option'),
]
listing_form.html
{% load widget_tweaks %}
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/htmx.org#1.6.1"></script>
</head>
<body>
<h1>Listing Form</h1>
<form method="post">
{% csrf_token %}
<div>
{{ form.option.label_tag }}
{% render_field form.option hx-get="/forms/option"
hx-trigger="change" hx-target="#option" hx-swap="outerHTML" %}
</div>
<div id="option"></div>
<input type="submit" value="Send">
</form>
<script>
document.body.addEventListener('htmx:configRequest', (event) =>
{
event.detail.headers['X-CSRFToken']='{{csrf_token}}';
})
</script>
</body>
</html>
partial_option.html:
{% if option %}
{% if option =='1' %}
<p>You have chosen option 1</p>
{% elif option == '2' %}
<p>You have chosen option 2</p>
{{ form.package.label_tag }}
{{ form.package }}
{% elif option == '3' %}
<p>You have chosen option 3</p>
{{ form.number.label_tag }}
{{ form.number }}
{{form.period.label_tag }}
{{ form.period }}
{% endif %}
{% else %}
<p>You have no option</p>
{% endif %}
{{ form.title.label_tag }}
{{ form.title }}
You have set the hx-swap="outerHTML" method, so HTMX will replace the target element with the response. Since your response does not contain a new <div id="option"> element, after the first request/swap cycle HTMX cannot find the target.
To solve this issue, change the swap method to innerHTML or embed the response in a <div id="option"></div> element.

How to use the class based view to render 2 different forms on one template in Django?

In the django basic polls app. I have added the functionality to add polls and I want the "add choice" form to come below the "poll detail" form on the template, how do i do it?
I want the "add choice" to render below the Vote of "detail" template
Add choice view:
def choice_view(request,question_id):
if request.user.is_staff and request.user.has_perm('poll.add_choice'):
question=Question.objects.get(pk=question_id)
if request.method=='POST':
cform=choice_form(request.POST)
if cform.is_valid():
add_poll=cform.save(commit=False)
add_poll.question=question
add_poll.vote=0
add_poll.save()
cform.save()
return HttpResponseRedirect(reverse('polls:add_choice',args=(question_id,)))
else:
cform=choice_form()
return render(request,'polls/add_choice.html',{'cform':cform,'question_id':question_id},context_instance=RequestContext(request),)
else:
return HttpResponse('Permission Denied')
Add choice template:
{% load crispy_forms_tags %}
{% include 'polls/base2.html' %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Add Choices</title>
</head>
<body>
<h1>Add Choices</h1>
<form method="post">
{% csrf_token %}
{{ cform.choice_text | as_crispy_field }}
<button class="btn btn-primary btn-default" type="submit">Add Next</button>
Done
</form>
</body>
</html>
Add choice form:
class choice_form(forms.ModelForm):
class Meta:
model=Choice
fields=['choice_text']
Poll detail view:
class DetailView(generic.DetailView):
model=Question
template_name = 'polls/detail.html'
Poll detail template:
{% load staticfiles %}
{% include 'polls/base2.html' %}
{% load crispy_forms_tags %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>{{ question.question_text }}</h1>
{% if error_message %}
<p><strong>{{ error_message }}</strong></p>
{% endif %}
<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
{% for choice in question.choice_set.all %}
{% if choice.is_voted_by%}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }} " checked="checked" />
{% else%}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }} " />
{% endif%}
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br/>
{% endfor %}
<br><button class="btn btn-lg btn-primary btn-block" type="submit" style="width: 100px">Vote</button>
</form>
<br>Leave a comment
{% if user.is_staff %}
Delete question
{% endif %}
<hr>
<h3>Comments</h3>
{% for comment in question.comments.all %}
<br>{{ comment.body }}
{% if user.is_staff %}
Delete comment
{% endif %}
<br>--{{ comment.email }}<br>
{% endfor %}
</body>
</html>

How to delete files in Django?

I followed this tutorial: Need a minimal Django file upload example
Obviously it works. But I want to be able to delete a file as well. Now even if I delete it manually from disc, it still appears on list, even after reconnecting to a server (why?)
I changed the list.html file by adding another form in the loop:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Minimal Django File Upload Example</title>
</head>
<body>
<!-- List of uploaded documents -->
{% if documents %}
<ul>
{% for document in documents %}
<li>{{ document.docfile.name }}
{% if user.is_staff %}
<form action="{% url 'delete' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="submit" value="Delete" />
</form>
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<p>No documents.</p>
{% endif %}
<!-- Upload form. Note enctype attribute! -->
<form action="{% url 'list' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ form.non_field_errors }}</p>
<p>{{ form.docfile.label_tag }} {{ form.docfile.help_text }}</p>
<p>
{{ form.docfile.errors }}
{{ form.docfile }}
</p>
<p><input type="submit" value="Upload" /></p>
</form>
</body>
</html>
As you can see, I added Delete button in a form. By doing so, I have a button near each file. I added this to my views :
def delete(request):
if request.method != 'POST':
raise HTTP404
else:
docId = request.POST.get('docfile', None)
if docId is not None:
docToDel = Document.objects.get(pk=docId)
docToDel.delete()
form = DocumentForm(request.POST, request.FILES)
documents = Document.objects.all()
return HttpResponseRedirect(reverse('myapp.views.list'))
But that does not do anything, just reloads the page.
As I said, now I cannot even delete them manually.
What am I doing wrong?
First of all file on disk and model in DB are different things. To delete file from disk and DB you may try this
from django.shortcuts import get_object_or_404
def delete(request):
if request.method != 'POST':
raise HTTP404
docId = request.POST.get('docfile', None)
docToDel = get_object_or_404(Document, pk = docId)
docToDel.docfile.delete()
docToDel.delete()
return HttpResponseRedirect(reverse('myapp.views.list'))
Also you forgot to specify ID of Document to delete
<!-- List of uploaded documents -->
{% if documents %}
<ul>
{% for document in documents %}
<li>{{ document.docfile.name }}
{% if user.is_staff %}
<form action="{% url 'delete' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="hidden" name="docfile" value="{{ document.pk }}" />
<input type="submit" value="Delete" />
</form>
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<p>No documents.</p>
{% endif %}

Avoid Django to strip text file upload

I've been asked to convert a Python application into a Django one but I'm totally new to Django.
I have the following problem, when I upload a file text that must be read to save its content into a database I find that Django is striping the "extra" whitespaces and I must keep those whitespaces.
This is my template
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
{% if newdoc %}
<ul>
{% for line in newdoc %}
<li>{{ line }} </li>
{% endfor %}
</ul>
{% endif %}
<form action="{% url 'exam:upload' %}" method="post" enctype="multipart/form-data" content-type="text/plain">
{% csrf_token %}
<p>{{ form.non_field_errors }}</p>
<p>{{ form.docfile.label_tag }} {{ form.docfile.help_text }}</p>
<p>
{{ form.docfile.errors }}
{{ form.docfile }}
</p>
<p><input type="submit" value="Upload" /></p>
</form>
</body>
This is what I have in the views.py
def upload(request):
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = request.FILES['docfile']
form = DocumentForm()
return render(request, 'exam/upload.html', {'newdoc': newdoc, 'form': form})
else:
form = DocumentForm() # A empty, unbound form
return render(request, 'exam/upload.html', {
'form': form,
})
And this is my forms.py:
from django import forms
class DocumentForm(forms.Form):
docfile = forms.FileField(
label='Select a file',
help_text='max. 42 megabytes'
)
Now when I upload the file, it shows a random line like this:
"09000021009296401 02 b a b a b b b d b b d d a +8589 +03+6942 +03+1461 +00+5093 +00+2 +00+9237 +01+60 +01+00 +00"
While it should be this:
"09000021009296401 02 b a b a b b b d b b d d a +8589 +03+6942 +03+1461 +00+5093 +00+2 +00+9237 +01+60 +01+00 +00 "
I must keep the extra spaces and they save this information into a database, which I cannot correctly do if I don't have all the spaces that the file has.
Also, before you ask, It is not related with the print format of Django, since in a previous test I already tryed to save the information into the model, but it has the same problem with spaces.
Thanks everyone.
Change the template as follow:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
{% if newdoc %}
<pre><code>{% for line in newdoc %}{{ line|safe }}{% endfor %}</code></pre>
{% endif %}
<form action="{% url 'exam:upload' %}" method="post" enctype="multipart/form-data" content-type="text/plain">
{% csrf_token %}
<p>{{ form.non_field_errors }}</p>
<p>{{ form.docfile.label_tag }} {{ form.docfile.help_text }}</p>
<p>
{{ form.docfile.errors }}
{{ form.docfile }}
</p>
<p><input type="submit" value="Upload" /></p>
</form>
</body>