django: pass an element to my template through context dictionary - django

in this view ...
def home(request):
context = {'page_title':'Volunteers', 'action':'<button class="btn btn-primary btn-small">Leave</button>'}
return render(request, 'app/home.html', context)
I try to send a button element (or some other HTML) through the context dictionary.
I add it to the template like this
<div class="row">
<div class="col-md-6"><h4>{{page_title}}</h4></div>
<div class="col-md-6 text-md-right mt-1">{{action}}</div>
</div>
When the page renders, it just shows the text and does not allow it to be HTML.
Any idea how I can get this to treat it as HTML?
Thanks, Django beginner here finally coming from the dark side (PHP) =)

Mark the string as HTML-safe:
{{page_title | safe}}
More about automatic HTML escaping in Django can be found here.
However, I would prefer another solution, where the button is described in the template, and conditionally rendered if needed, if it is one of small number of possible actions and they are all in the one place. If the buttons are required in multiple places, I would go with a custom Django template tag. Sending HTML from business code usually violates the principle of separation of code and presentation.

In your view you can use mark_safe method:
from django.utils.safestring import mark_safe
def home(request):
context = {
'page_title': 'Volunteers',
'action': mark_safe('<button class="btn btn-primary btn-small">Leave</button>')
}
return render(request, 'app/home.html', context)

Related

How to set up a loading screen for a form that takes time to process in Django?

I have the following view (with some code removed for simplicity):
def add_entry(request, num):
form = ModelForm1()
form2 = Form2()
if request.method == 'POST':
form = ModelForm1(request.POST)
form2 = Form2(request.POST)
if form.is_valid() and form2.is_valid():
text = form2.cleaned_data['text']
database_dict = process_data(text)
# Some code here that dictates how data is saved from the dictionary....
entry.save()
return redirect('entries', num)
return render(request, 'app/add_entry.html', {"form": form, "form2": form2})
Basically, this view contains a form which takes a text input. That text input is processed through a function with returns a dictionary of values. Those values are then stored in a database, and this database is rendered to the template "entries.html".
Depending on the amount of text in the form, it can take quite some time to process, and this time is spent in the input-screen of the form. I would like to add an interim "loading"-screen which the user is taken to while the form is processed, which then redirects to entires.html after the processing is done, but I can't seem to find out how. Adding a redirect without the "return" statement right after the data is processed doesn't seem to do anything, and adding the "return" messes everything up.
How do I do this?
Here's one approach using a Bootstrap 5 loader following Luiz's recommended approach in the comments. Add all of below code to Add_entry.html
Step 1: Add html element containing a loader
Make sure style="display: none;"
<div id="loadingModal" style="display: none;">
<button class="btn btn-primary" type="button" disabled>
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
Loading...
</button>
</div>
Step 2: Add js function to display loader when form is submitted
<script>
function openLoader() {
document.getElementById("loadingModal").style.display = "block";
}
</script>
Step 3: Assign js function to onsubmit= in form element
<form method="POST" onsubmit="openLoader()">
{% csrf_token %}
...form contents here...
</form>
When form is successfully submitted, the loading modal will appear, and then disappear when redirect kicks in as you stated.

Django - how to go back to previous view with parameters

I am relatively new with Django, this must be a common problem.
I have created a view to show a form to input date (using widget that returns separate fields):
when date is inserted, I call a function userPage(request, my_date)
that filters, processes and renders a page (user.html) showing a list of items.
def datePage(request):
user=request.user
context = {}
context['form'] = UserDateForm()
if request.GET:
date_yr = request.GET['food_date_year']
date_mo = request.GET['food_date_month']
date_day = request.GET['food_date_day']
my_date_string = date_yr+'-'+date_mo+'-'+date_day
my_date = datetime.strptime(my_date_string, "%Y-%m-%d").date()
return userPage(request,my_date)
return render(request, "date.html", context)
def userPage(request, my_date):
user=request.user
# process context using user, my_date
context={...:..., 'my_date': my_date}
return render(request,'user.html',context)
In user.html I include a URL to add an item:
</div>
<form action="{% url 'My_ItemCreate' %}" method="POST">
{%csrf_token%}
<button type="submit" class="btn btn-success">
<span class="glyphicon glyphicon-plus"></span>
</button>
</form>
</div>
'My_ItemCreate' points to a django.views.generic CreateView that creates an item.:
path('MyItemCreate/',views.My_ItemCreate.as_view(),name='My_ItemCreate'),
class My_ItemCreate(CreateView):
model = MyItem
fields = ...
After creating the item in the CreateView, how do I go back to the user page
after I inserted the date? I have lost the date in the new URL.
If I use URL resolver to go to userPage, how do I pass a date in the format?
It would be nice that I am able to pass initial values in the CreateView, and
make some fields read-only, how do I modify/override CreateView ?
Many Thanks for your help!
I have found an answer to my problem: using request.session
to store a value and retrieving in other views, it works fine.
I am still curious to know if there are experts who
would provide a more elegant solution, and if someone
could be so kind to answer point 2) regarding CreateView read_only fields
Thanks
D

Django Formview Won't Validate

Django newbie here. I recently implemented a simple search with Django. I user specifies data in search bar, search is executed. I was able to determine today how to get the search to be null if user clicks on empty search bar. What I'm really trying to accomplish is to implement error logic so that the user has to specify data in the search bar. This has been very straight forward with all of the other views, but the search is a bit trickier, for me anyway. I have done several SO searches today, and determined that my HTML get method is ok for a search, but it may be causing some problems with the FORMVIEW that I am using. I have also tried to override POST and GET for the FORMVIEW, but can't seem to get it to work. I want to stick with CLASS BASED VIEWS, so I am using FORMVIEW. Here is my code....
My HTML
<form method="GET" autocomplete=off action="{% url 'Book:book_request_search_results' %}" >
<div>
<h1 class="title">Book Request Search</h1>
</div>
{{ form.non_field.errors }}
<div class="section">
<input type="search" class="name2" name="q">
</div>
<div class="section">
<input type="submit" id="" class="submit6" value="Search">
</div>
My VIEWS.PY
class BookRequestSearchView(LoginRequiredMixin,FormView):
form_class = BookRequestSearch
template_name = 'store/Book_request_search.html'
success_url = reverse_lazy('store:book_request_search_results')
def form_valid(self, form):
self.kwargs['q'] = form.cleaned_data['q']
if self.request.GET.get('q'):
pass
else:
raise forms.ValidationError("Enter data")
return super(BookRequestSearchView, self).form_valid(form)
My FORMS.PY
class BookRequestSearch(forms.Form):
q = forms.CharField(required=True)
def __init__(self, *args, **kwargs):
super(BookRequestSearch, self).__init__(*args, **kwargs)
I also tried to add a clean method to my form but that doesn't seem to work either...
def clean(self):
search = self.initial['q']
if search:
pass
else:
raise forms.ValidationError("Enter data")
return self.cleaned_data
I've been scanning most of the afternoon and can't seem to find a way to trigger validation on the FORMVIEW to trigger an error on the search bar. Any help would be appreciated. I've seen many different FORMVIEW articles, but none of them have helped me understand what I'm doing wrong and why this isn't working. The way the code is now, it works, but it doesn't prevent user from clicking on search and then triggering essentially an empty query. I'm trying to prevent that by forcing the user to put something in the box if they try to search with no criteria. Thanks in advance for any direction.
I was wrong on saying that it's only possible with javascript.
It's so simple as adding the required attribute to your input form, so the browser catches the empty error.
<form>
<input type="search" required><!-- added the required attribute-->
</form>

Alternative ways of styling class-based form fields

In Django, one applies CSS styling to class-based form fields in forms.py (or equivalent).
My question: is it impossible to do it any other way inside a Django project?
I'll accept the answer even if the answer is "it's impossible". Hacks and tricks are acceptable as well. Illustrative examples would be great.
p.s. here's an example of a Django form where I've styled in the class-based form:
class SampleForm(forms.Form):
description = forms.CharField(max_length=250)
def __init__(self,*args,**kwargs):
super(SampleForm, self).__init__(*args,**kwargs)
self.fields['description'].widget.attrs['class'] = 'btn bcg'
self.fields['description'].widget.attrs['style'] = 'background-color:#F8F8F8; width:98%; color: #1f8cad;'
self.fields['description'].widget.attrs['autocomplete'] = 'off'
You can use template tags.
css.py
from django import template
register = template.Library()
#register.filter(name='css')
def css(field, css):
return field.as_widget(attrs={"style":css})
in your template:
{% load css %}
{{ item.field|css: 'width: 100px' }}
the result could be
<input id="id_field" name="field" style="width: 100px" type="text" />
As you can see, in style is your variable (width: 100px). You can also do it with class.

Render the same template with different text?

I have about 4-5 templates for my Django project. I want to use each template across about 10 pages(with 10 different sets of content/text) each.
I've been using contexts to fill out the relevant content on each page when it is rendered.
However, I'm not sure about any potential performance loss because the content is quite large. Is there a neat and efficient way to render one template with content relevant to a link clicked on a previous page.
what I'm currently doing:
index.html
<div class='well well-sm well-md well-lg'>
<h1> {{variable1}} </h1>
</div>
views.py
def page1(request):
variable1 = 'Long text for each variable. Many variables on each page so
lots of long text overall. '
return render(request, 'index/base.html', {'variable': variable1})
def page2(request):
variable1 = 'More long text. '
return render(request, 'index/base.html', {'variable': variable1})
Basically I want to know if there is a way of doing the above without having to use contexts. Or will I have to create a template for each page?
Check django tutorial doc
In url:
url(r'^(?P<page_id>[0-9]+)/$', views.page, name='page'),
In view:
def page(request, page_id):
// have a dict or list, get result using the page_id
return render(request, 'index/base.html', {'variable': variable})
Or if you don't have a page_id/name system, you can use:
def page(request):
url = request.path
value = dict[url]
render (...)
You need to solve in 2 steps.
Determine previous page, so that you choose appropriate text.
You can solve this in the view. Just set some context variable to be passed to templates. I believe this is not easy to do in template processing, view is better.
Sample code
def page(request):
if previous_page == "some page 1"
page_type = 1
elif previous_page == "some page 2"
page_type = 2
return render(request, 'index/base.html', {'page_type': page_type})
Then in template use {%include%} tag to use appropriate template.
Sample index.html
<div class='well well-sm well-md well-lg'>
{% if page_type == 1 %}
{%include "page_type1.html"%}
{%else%}
{%include "page_type2.html"%}
{%endif
</div>
You need to write 2 templates page_type1.html and page_type2.html for render required html.