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.
Related
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)
What is the recommended way to write views (as functions) in Django?
I am asking in terms of readability, etc.
For example: define the template first, then do the translations, then define models and lastly define context.
Here is some example code:
def index(request): # Landing page, translated to the browser's language (default is english)
template = loader.get_template("koncerti/index.html")
# Translators: This is the text on the homepage buttons
concerts = gettext("Koncerti")
band = gettext("Band")
# Translators: This is the option in the language-switch box
foreignLanguage = gettext("eng")
koncertiUrl = '/koncerti/international' # The URL slug leading to 'koncerti' page
bandUrl = '/band/international' # The URL slug leading to 'band' page
translationMode = '' # The URL slug that is leading to slovenian translation mode
context = {
'Concerts' : concerts,
'Band' : band,
'foreignLanguage' : foreignLanguage,
'koncertiUrl' : koncertiUrl,
'bandUrl' : bandUrl,
'translationMode' : translationMode
}
return HttpResponse(template.render(context, request))
I think you are doing too much in the view. A view is supposed to make the proper querysets, and make changes to the database. It's main task is thus to decide what to render, and what to update. Not how to update it.
Deciding about translations, url resolution, etc. is normally a task of the template. The template has template tags for that such as {% trans … %} [Django-doc] and {% url … %} [Django-doc].
Using the {% url … %} over raw URLs you construct yourself is strongly advisable. It makes the odds of making a mistake smaller, and furthermore it is clear to what view you are referring.
You furthermore might want to use render(..) [Django-doc]. This function combines looking for a template, rendering that template with the template render engine, and returning a HTTP response.
from django.shortcuts import render
def index(request):
return render(request, 'koncerti/index.html', {})
In the template, you can then render this with:
{% load i18n %}
{% trans 'Koncerti' %}
I am using Django 2 and I would like to display in a single article blog page both the body of that article and, at the bottom, 3 recommended articles.
Unfortunately the display 3 articles part doesn't work. I receive no error it just doesn't display any part of the block from the loop more specifically:
my view
def detail (request, post_slug):
post = get_object_or_404 (Post, slug=post_slug)
suggested = Post.objects.all()[:3]
return render (request, 'detail.html', {'post':post}, {'suggested':suggested})
and the html to display the suggested
<section class="read-next">
{% for a in suggested.all %}
<a href="/{{a.slug}}" class="prev-post " style="background-image:url({{a.image}})" >
<div class="info">
<div class="tag">We recommend</div>
<h3 class="post-title">{{a.title}}</h3>
</div>
</a>
{% endfor %}
</section> <!-- .read-next -->
nothing gets rendered of this section or for wherever I add the loop.
Thanks in advance for your help!
A couple things are the issue here.
render is called wrong. The format of render is like so:
return render(request, template_name, context)
context is a single dictionary that can be used to place variable values on the html page.
You are sending two separate dictionaries to render. So context right now for you is only a dictionary with one key: "post". The dictionary that contains suggested is set to content_type instead of being sent to context.
So your view needs to become:
def detail (request, post_slug):
post = get_object_or_404 (Post, slug=post_slug)
suggested = Post.objects.all()[:3]
context = {
"post": post,
"suggested": suggested
}
return render (request, 'detail.html', context)
Because you sliced the queryset of Post objects (suggested = Post.objects.all()[:3]), it is now a queried list, not a usable queryset. So you treat it as if it's a list. Basically what this means, is that you don't use {% for a in suggested.all %} because suggested no longer has a method called all after slicing it.
So your template should use {% for a in suggested %} instead of {% for a in suggested.all %}. The reason it didn't work this correct way before is because suggested wasn't even in the context before because of issue #1.
When you call render you should return a single context dictionary:
return render(request, 'detail.html', {'post':post, 'suggested':suggested})
The fourth argument to the render shortcut is content_type, so your current code is equivalent to:
return render(request, 'detail.html', context={'post':post}, content_type={'suggested':suggested})
I have a view:
def partnered_item_edit(request, item_id, return_to='delivery_manager'):
item = get_object_or_404(PartneredItem, pk=item_id)
...
delivery.save()
return redirect(return_to)
The purpose of this is to make it reusable so I can use this view from different places in the program and have it redirect to the view where the user was working previously.
In the template I have:
<p><a href="{% url 'partnered_item_edit' item_id=item.id return_to='delivery_view' %}" class="btn btn-default ...
The view I currently want to redirect to is declared like this:
def delivery_view(request, delivery_id):
delivery = get_object_or_404(Delivery, pk=delivery_id)
...
but I will also want it to redirect to this at other parts of the program such as:
def delivery_manager(request):
date = datetime.date.today()
(this one does not require the delivery_id)
I would also like to hook this view into other templates as well, and use this technique for other redirect / view combos. Basically I'm looking for something that allows me to do:
reverse('delivery_view', args=('delivery_id'))
or
reverse('delivery_manager')
in the template.
Then again... am I trying to make this work the hard way? Do I need to make a template filter to make this work?
Thanks for your help.
I'd suggest you to change a bit logic of partnered_item_edit view:
def partnered_item_edit(request, item_id, return_to_delivery=False):
item = get_object_or_404(PartneredItem, pk=item_id)
...
delivery.save()
if return_to_delivery:
return redirect('delivery_view', args=(delivery.pk,))
else:
return redirect('delivery_manager')
If you want to edit an item and redirect to delivery manager use in template:
{% url 'partnered_item_edit' item_id=item.id %}
Otherwise if you want to redirect to delivery use:
{% url 'partnered_item_edit' item_id=item.id return_to_delivery=True %}
Hope these helps!
In my Django app, the user is presented a simple form, on validation the view searches the data (some web scraping) and returns a template in the context of which we added a list of the results, we present them to the user and he/she chooses one and (this is my problem) I want to get that choice back to another view (now we want to add the selected object to the database).
I identified my problem, but I really think I'm missing something and not using Django the right way. For now this is how I display the data and put it back in a form: (jade template. Of course I do this with many more attributes so the template is quite big and I have to add an hidden input for each value I want back)
for b in result_list
h4
for aut in b.authors
div {{ aut }}
form(action='/add/', method='post') {% csrf_token %}
{% csrf_token %}
input(type='hidden', value="{{ b.authors }}", name='authors')
edit2: there is one button per book. I am just trying to return one book.
It works fine when authors has a single element, but with more than once the template doesn't return a valid json. I could give you the structure but I tend to think this is not the point.
What am I missing ? How to display search results and get the selected one to another view ?
Do I miss something about forms ? Shall I use a session variable, a context processor with middleware ? I guess the hidden input is wrong: how can I do differently ?
many thanks
edit: below I show where my problem is. However, maybe I shouldn't fix that problem but use Django the right way instead.
1- my 1st view renders a template and feeds it with the search results:
return render(request, "search/search_result.jade", {
"return_list": book_list,
})
book_list is a list of dicts where authors is a list of unicode str:
retlist[0]["authors"]
>>> [u'Ayroles, Alain']
2- my template is above. It displays a form and a hidden input. The goal is to send back a book: a dictionnary with the title, the editor… and a list of authors.
3- now the users clicks "Add this book", we are in the "add" view. Let's inspect the authors:
req = request.POST.copy()
authors = req["authors"]
>>> u"[u'Ayroles']" # note the u after the [
# this is not a valid json, I can't get a list out of it
import json
json.loads(req["authors"])
>>> *** ValueError: No JSON object could be decoded
So I passed a list from the first view to the template, I parsed it and displayed it, but the template doesn't return a valid list/json to the second view in the POST parameters and I can't extract my authors. I don't know what to think:
else what I want to do is fine and I just have to better handle my data structure
else I shouldn't be doing like that and use more of Django: use more forms ? sessions ?
This is how I see it. It is not really an answer, but it's already too much code to put into comments
Template:
for b in result_list
h4
for aut in b.authors
div {{ aut }}
form(action='/add/', method='post')
{% csrf_token %}
input(type='hidden', value="{{ b.authors }}", name='authors')
input(type='submit', value="Add book")
View:
if request.method == 'POST':
authors = request.POST.get['authors']
#do something with authors, e.g. create a JSON string:
authors_JSON = json.dumps(authors)
Same logic, using Django forms:
View:
if request.method == 'POST':
book = BookForm(request.POST)
#do something with authors, e.g. create a JSON string:
authors_JSON = json.dumps(book.authors)
Forms.py:
class ContactForm(forms.Form):
authors = forms.CharField(max_length=100)
#etc, possibly including custom __init__ logic
That 'u' thing, it happens because you do json.loads() on an object. If you want to serialize, it should be json.dumps(), otherwise array converted to string and then treated as JSON, that is why Python unicode mark got there (i.e. not a template bug)
I finally got it: we can easily share data between views using the session.
The session is activated in the default Django install. We just have to choose a session engine: in a temporary file, in memory,…
SESSION_ENGINE = 'django.contrib.sessions.backends.file'
Now in my search/ url I can add a variable in request.session, a dictionnary-like object:
search_results = compute_and_get_data()
request = search_results()
I feed my template with the search_results list. In my template, I iterate on it and I use a single hidden input field to return the counter of the for loop.
for b in search_results
table
tr
td
h4 {{b.authors}}
form(action='/add/', method='post') {% csrf_token %}
{% csrf_token %}
input(type='hidden', value="{{ forloop.counter0 }}", name='forloop_counter0')
input.btn.btn-primary(type='submit', value='add book', title="add that book to my collection")
On submit, we enter the add/ view. Here I can get the book the user selected:
forloop_counter0 = int(request.POST["forloop_counter0"])
book = request.session["search_result"][forloop_counter0]
Now my book is a dictionnary with a lot of information. I didn't have to handle json formatting and the like.
This question helped me:
Django Passing data between views
How do you pass or share variables between django views?