Django read cookie in template tag - python

Is it posible to read a cookie in a template tag?
I set the cookie in a middleware but I'd like to read in a template tag.
def process_response(self, request, response):
response.set_cookie('changed', 'yes')
response.set_cookie('choose', request.LANGUAGE_CODE)
return response
Thanks

Since cookies are key/value pairs, you can read the value of a cookie in a template using the dot notation as shown below.
In your views:
def process_response(self, request, response):
response.set_cookie('changed', 'yes')
response.set_cookie('choose', request.LANGUAGE_CODE)
return response
And in your template:
{{ request.COOKIES.cookie_name }}

Marcos' answer should be accepted.
Here's how I used Marcos' answer in my case:
1 - Django view (in views.py):
from django.shortcuts import render_to_response
def helloWorld(request):
response = render_to_response("awesomeapp/hello.html", {
'person_name': "Mark"
}, context_instance=RequestContext(request))
person_age = 26
response.set_cookie('the_new_number', person_age, max_age = 5000000) # 5million secs is almost two months, adjust as appropriate
return response
2 - In marktags.py:
# To read cookies within django template, from http://stackoverflow.com/questions/26301447/django-read-cookie-in-template-tag
#register.simple_tag(takes_context = True)
def cookie(context, cookie_name): # could feed in additional argument to use as default value
request = context['request']
result = request.COOKIES.get(cookie_name,'') # I use blank as default value
return result
Note that marktags.py file, where I have all my custom filters, is stored inside the templatetags directory
(In my case, I use django-allauth so I saved my custom file marktags.py beside the template_tags.py file in myapp/allauth/account/templatetags folder)
3 - Finally, hello.html includes the following:
<head>
{% load marktags %}
</head>
<body>
{{ person_name }} is {% cookie 'the_new_number' %} years old.
</body>
Note the {% syntax, not {{ for accessing the cookie
Here's another example
If you're new to cookies, I recommend an extension such as
EditThisCookie.
You can see only the server understood how the person's name was displayed, but the browser's cookie contains the value of the person's age

You have to create a template tag with takes_context
#register.simple_tag(takes_context = True)
def custom_template_tag_name(context, value1, value2):
request = context['request']
result = request.COOKIES.get('cookie_name', '')
return result

Related

How to make an edit view using django python?

I have edit_client view, Client model and a ClientForm. What I need is to edit an existing record from Client but display it as an editable form, and save the updated record. What should be seen in my views.py and my edit_client.html?
You can create a function named : edit_client into your view file.
As an example, you can use a link in your html like this :
<a href="{% "edit_client" client.pk %}> {{ client.name }} </a>
And your function can be :
def edit_client(request, client_id):
client = Client.objects.get(pk=client_id)
clients = Client.objects.all()
if request.method = "POST":
# what you want to edit (name, age etc ...)
client.save()
return render_to_response('index.html', {"clients":clients}, context_instance=RequestContext(request))
else:
return render_to_response('edit_client.html', {"client":client}, context_instance=RequestContext(request))
Note that it will be different if you want to use a form.

How can a custom jinja2 tag interface with the context of a flask request

I'm writing a custom jinja2 extension to use in flask applications and I'm looking for a way to access the templates context data using the tag I'm implementing. That is, I want the extension tag to use context params passed into the template:
#app.route('/users/<user_id>')
def user_page(user_id):
...
return render_template('users/index.html', user_id=user_id, active=True)
The template:
<!-- I want this tag to see the value of user_id and active -->
{% my_jinja2_tag %}
I know I can render the context variable using {{ user_id }}, but what I'm looking for is a way to inspect the context of the template rendering a custom jinja2 extension. Is that doable? thanks.
ContextReference
Yes, it's possible using jinja2.nodes.ContextReference(). See the API reference here.
Relax, I'm going to guide you through it. :)
First the extension:
class ActiveCheckerExtension(jinja2.ext.Extension):
"""
This will give us a {% check_active %} tag.
"""
template = 'Active is : %s'
tags = set(['check_active'])
def _render_tag(self, context, caller):
return jinja2.Markup(self.template % unicode(context['active']))
def parse(self, parser):
ctx_ref = jinja2.nodes.ContextReference()
lineno = next(parser.stream).lineno
node = self.call_method('_render_tag', [ctx_ref], lineno=lineno)
return jinja2.nodes.CallBlock(node, [], [], [], lineno=lineno)
Then let's add it to Flask's jinja2.
app.jinja_env.add_extension(ActiveCheckerExtension)
Now in your template, you can do:
{% check_active %}
Make sure active is defined in all the templates you add the tag to, or else you'll get a KeyError because the context won't have that template variable.

getting an error didn't return an HttpResponse object. What is going on?

For the first time i am trying to create a small django application. It is just a basic page with two forms, a search form, which returns values from a database (working well), and a insert form (the problem). In the insert form there are three text fields asking for values for a new row in the database. The app is called "searcher". Here is my code:
views.py
from django.http import HttpResponse
from django.template import Context
from django.template.loader import get_template
from searcher.models import Publisher
from django.db.models import Q
def search(request):
if "search" in request.GET:
value = request.GET['search']
results = Publisher.objects.filter(Q(city__contains=value) | Q(name__contains=value))
else:
value = "No term"
results = []
template = get_template("base.html")
html = template.render(Context({"value":value, "results":results}))
return HttpResponse(html)
def insert(request):
if "name" in request.POST:
for key in request.POST:
counter = 0
if key != '':
counter = counter + 1
if counter == len(request.POST):
row = Publisher(name=request.POST['name'], city=request.POST['city'], website=request.POST['website'])
row.save()
base.html
<html>
<body>
<form method = "GET" action = "">
<input type = "text" name = "search"><input type = "submit">
</form><br>
you searched for:{{value}}<br>
your results:
{% for result in results %}
<br>{{result}}<br>
{% endfor %}
<br><br>
<form method = "POST" action = "/test/insert/">
<input type = "text" name = "name" value = "name"><br>
<input type = "text" name = "city" value = "city"><br>
<input type = "text" name = "website" value = "website"><br>
<input type = "submit">
</form>
</body>
</html>
urls.py
from django.conf.urls import patterns, include, url
from searcher import views
urlpatterns = patterns('',
url(r'^test/$', views.search),
url(r'^test/insert/$', views.insert),
)
the error is saying "ValueError at /test/insert The view searcher.views.insert didn't return an HttpResponse object." as a result of me submitting the insert form.
I understand what the error is saying: sorry but all views in views.py MUST return something to show.
Firstly why is this?
And secondly what is the correct way to accomplish the insert form ? Currently i have set the form action = "/test/insert/" so that in urls.py it can recognise the insert form was submitted and call the function insert, how should the function be called instead?
You seem to be asking two questions
"Why must all views return something to show?"
You're misinterpreting the error message. The error you saw didn't
say "all views MUST return something to show" - it only said
"searcher.views.insert didn't return an expected HttpResponse".
Since you're dealing with a HTTP request (a form POST), you're
expected to send back a HTTP Response. All HTTP requests expect a
response - this is a matter of a standard agreement on how to
communicate - the details are part of the http standard
definition, and somewhat explained as part of this answer.
"What is the correct way to accomplish the insert form?"
You're implementing it correctly - just send back a HTTP Response
for the POST. The code for it is almost identical to what you're
sending back in response to the Search request (maybe you'll want to
add a message in saying that the insert completed successfully,
where in the Search request you might have returned the item that
was being searched for?).
If you'd like to avoid having to refresh the form altogether, you might want to use an ajax call, instead of a http call, again, as described here.
All views must return an HttpResponse object because that's what the server returns to the browser that initially makes the request. Pretty much by definition, a view is a callable that accepts a request object and returns a response object.
It's fine to have code in views.py that doesn't return a response, such as helper functions for a view, but it's not fine to use such code as a view.
The usual pattern when working with POST requests is to return a redirect to a success page, on success, and to redisplay the form with the appropriate error messages on failure. See for instance the "Using a form in a view" docs.

Python, Flask, and jinja templates - How to iterate over a dictionary created server side

I am using flask.
On the server server when a page loads I create a dictionary.
#app.route('/edit_creative', methods=["GET", "POST"])
#login_required
def edit_creative():
if request.method == "POST":
pass
query = """select * from mystable"""
print query
rows = execute_query(query,select=True)
creative_handler={}
for row in rows:
j = row[2].strip("'")
j = json.loads(j)
creative_handler[row[1]]=j
return render_template("edit_creatives.html",title = 'Edit Creative')
On the client side I want to iterate over the hash:
{% for crid, object in creative_handler.iteritems() %}
{{ crid }}<br>
{% endfor %}
On the page I get this error
UndefinedError: 'creative_handler' is undefined
So..how do I use jinja templates to iterate over a hash creates server side?
You need to pass creative_handler to the template:
return render_template("edit_creatives.html", title='Edit Creative', creative_handler=creative_handler)
Well you need to pass in the variable(s) you want to use, in the template.
>>> from flask import render_template
>>> help(render_template)
render_template(template_name, **context)
Renders a template from the template folder with the given
context.
:param template_name: the name of the template to be rendered
:param context: the variables that should be available in the
context of the template.
so return render_template("edit_creatives.html",title = 'Edit Creative', creative_handler = creative_handler)
Try
return render_template("edit_creatives.html",title = 'Edit Creative', creative_handler = creative_handler)
If creative_handler contains your data anyway.
You have to actually pass the object to the template so it can be seen and give it a name which you then use in the template. Also FYI the code is not executed client side, it's built inside your app then sent to the client. They just see the HTML that results from the loop etc.

Paginating the results of a Django forms POST request

I'm using Django Forms to do a filtered/faceted search via POST, and I would like to Django's paginator class to organize the results. How do I preserve the original request when passing the client between the various pages? In other words, it seems that I lose the POST data as soon as I pass the GET request for another page back to my views. I've seen some recommendations to use AJAX to refresh only the results block of the page, but I'm wondering if there is a Django-native mechanism for doing this.
Thanks.
If you want to access the store data in later request, you would have to store it somewhere. Django provides several ways to archive this:
1) You can use sessions to store the query: Every visitor who visits your site will get an empty session object and you can store whatever you want inside this object, which acts like a dict. Drawback: A single visitor can't do multiple searches with pagination concurrently.
2) Use cookies: If you set a cookie which is stored on the client side, the browser will append the data of the cookie to each request where you can access it. Cookies are more server friendly, because you don't need a session manager for them on the server, but the data stored in cookies is visible (and editable) to the client. Drawback: same as before.
3) Use hidden fields: You can add a form with some hidden fields on your search-result page and store the query inside them. Then, the client will resend the query whenever you submit the form. Drawback: You must use a form with submit buttons for the pagination on your page (simple links wont work).
4) Create Links which contain the query: Instead of using POST, you can also use GET. For example, you could have a link like "/search/hello+world/?order=votes" and "paginated links" like "/search/hello+world/2/?order-votes". Then the query can be easily retrieved from the URL. Drawback: The maximum amount of data you can send via GET is limited (But that shouldn't be a problem for a simple search).
5) Use a combination: You might want to store all the data in a session or a database and access them via a generated key which you can put in the URL. URLs might then look like "/search/029af239ccd23/2" (for the 2nd page) and you can use the key to access a huge amount of data which you have stored before. This eliminates the drawback of solution 1 as well as that of solution 4. New drawback: much work :)
6) Use AJAX: With ajax you can store the data inside some js-variables on the client side, which can then passed to the other requests. And since ajax will only update your result list, the variables aren't getting lost.
Reading the very nice answer from tux21b I decided to implement the first option, i.e., to use the session to store the query. This is an application that searches real estate databases. Here is the view code (using django 1.5):
def main_search(request):
search_form = UserSearchForm()
return render(request, 'search/busca_inicial.html', {'search_form': search_form})
def result(request):
if request.method == 'POST':
search_form = UserSearchForm(request.POST)
if search_form.is_valid():
# Loads the values entered by the user on the form. The first and the second
# are MultiChoiceFields. The third and fourth are Integer fields
location_query_list = search_form.cleaned_data['location']
realty_type_query_list = search_form.cleaned_data['realty_type']
price_min = search_form.cleaned_data['price_min']
price_max = search_form.cleaned_data['price_max']
# Those ifs here populate the fields with convenient values if the user
# left them blank. Basically the idea is to populate them with values
# that correspond to the broadest search possible.
if location_query_list == []:
location_query_list = [l for l in range(483)]
if realty_type_query_list == []:
realty_type_query_list = [r for r in range(20)]
if price_min == None:
price_min = 0
if price_max == None:
price_max = 100000000
# Saving the search parameters on the session
request.session['location_query_list'] = location_query_list
request.session['price_min'] = price_min
request.session['price_max'] = price_max
request.session['realty_type_query_lyst'] = realty_type_query_list
# making a query outside the if method == POST. This logic makes the pagination possible.
# If the user has made a new search, the session values would be updated. If not,
# the session values will be from the former search. Of course, that is what we want because
# we want the 'next' and 'previous' pages correspond to the original search
realty_list_result = FctRealtyOffer.objects.filter(location__in=request.session['location_query_list']
).filter(price__range=(request.session['price_min'], request.session['price_max'])
).filter(realty_type__in=request.session['realty_type_query_lyst'])
# Here we pass the list to a table created using django-tables2 that handles sorting
# and pagination for us
table = FctRealtyOfferTable(realty_list_result)
# django-tables2 pagination configuration
RequestConfig(request, paginate={'per_page': 10}).configure(table)
return render(request, 'search/search_result.html', {'realty_list_size': len(realty_list_result),
'table': table})
Hope it helps!If anyone has any improvement to suggest, be welcome.
As #rvnovaes, a way to use session to solve the matter.
The drawback of his solution is that if there are many search fields you have to write many lines of code, and also if you show the search form in the result page, all the fields will be blank, while they should keep their values.
So I'd rather save all the post data in session, and at the beginning of the view force the value of request.POST and request.method if a session is defined:
""" ... """
if not request.method == 'POST':
if 'search-persons-post' in request.session:
request.POST = request.session['search-persons-post']
request.method = 'POST'
if request.method == 'POST':
form = PersonForm(request.POST)
request.session['search-persons-post'] = request.POST
if form.is_valid():
id = form.cleaned_data['id']
""" ... """
More info here
I did this in my web application with get parameters Maybe i can help you :
Views.py
class HomeView(ListView):
model = Hotel
template_name = 'index.html'
paginate_by = 10 # if pagination is desired
def get_queryset(self):
qs = super().get_queryset()
kwargs = {}
if 'title' in self.request.GET:
title = self.request.GET.get('title')
if title != '':
kwargs['title__icontains'] = title
if 'category' in self.request.GET:
category = self.request.GET.get('category')
if category:
kwargs['category_id'] = category
if 'size' in self.request.GET:
size = self.request.GET.get('size')
if size:
kwargs['size_id'] = size
if 'service' in self.request.GET:
service = self.request.GET.get('service')
if service:
kwargs['service_id'] = service
if 'ownership' in self.request.GET:
ownership = self.request.GET.get('ownership')
if ownership:
kwargs['ownership_id'] = ownership
qs = qs.filter(**kwargs)
return qs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
form_init = {}
form = SearchForm()
if self.request.GET.items():
try:
parameters = self.request.GET.items()
except KeyError:
parameters = {}
for key, value in parameters:
for field in form.fields:
if key == field:
form_init[key] = value
form.initial = form_init
if 'title' in self.request.GET:
title = self.request.GET.get('title')
if title != '':
context.update({
'title': title
})
if 'category' in self.request.GET:
category = self.request.GET.get('category')
context.update({
'category': category
})
if 'size' in self.request.GET:
size = self.request.GET.get('size')
context.update({
'size': size
})
if 'service' in self.request.GET:
service = self.request.GET.get('service')
context.update({
'service': service
})
if 'ownership' in self.request.GET:
ownership = self.request.GET.get('ownership')
context.update({
'ownership': ownership
})
context.update({
'search_form': form
})
return context
Pagination file html
<div class="row">
{% if is_paginated %}
<nav aria-label="...">
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item"><a class="page-link" href="?category={{category}}&size={{size}}&service={{service}}&ownership={{ownership}}&page={{ page_obj.previous_page_number }}">Previous</a></li>
{% else %}
<li class="page-item disabled"><span class="page-link">Previous</span></li>
{% endif %}
<span class="page-current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
<li class="page-item"><a class="page-link" href="?category={{category}}&size={{size}}&service={{service}}&ownership={{ownership}}&page={{ page_obj.next_page_number }}">Next</a></li>
{% else %}
<li class="page-item disabled"><span class="page-link">Next</span></li>
{% endif %}
</ul>
</nav>
{% endif %}
</div>
You can ask request object if it's ajax, simply request.is_ajax. This way you can detect, whether it's first post request or further questions about the next pages.
Have the search form and the results display on one single django template. Initially, use css to hide the results display area. On POSTing the form, you could check to see if the search returned any results and hide the search form with css if results exist. If results do not exist, use css to hide the results display area like before. In your pagination links, use javascript to submit the form, this could be as simple as document.forms[0].submit(); return false;
You will need to handle how to pass the page number to django's paging engine.
My suggestion would be to store the post request using a session or a cookie. In case the post data is sensitive, you should use session to store it. The code below contains my logic to implement it using session.
def index(request):
is_cookie_set = 0
# Check if the session has already been created. If created, get their values and store it.
if 'age' in request.session and 'sex' in request.session:
age = request.session['age']
sex = request.session['sex']
is_cookie_set = 1
else:
# Store the data in the session object which can be used later
request.session['age'] = age
request.session['sex'] = sex
if(request.method == 'POST'):
if(is_cookie_set == 0): # form submission by the user
form = EmployeeForm(request.POST)
sex = form.cleaned_data['sex']
age = form.cleaned_data['age']
if form.is_valid():
result = Employee.objects.all(sex=sex,age_gte=age) # filter all employees based on sex and age
else: # When the session has been created
result = Employee.objects.all(sex=sex,age_gte=age)
paginator = Paginator(result, 20) # Show 20 results per page
page = request.GET.get('page')
r = paginator.get_page(page)
response = render(request, 'app/result.html',{'result':result})
return response
else:
form = EmployeeForm()
return render(request,'app/home.html',{'form':form})
You should also check if the post fields are empty or not and change the logic according to it. You can also store the whole post request in the session as suggested by #abidibo.
You can also use cookies for the same. I have explained it here
The Below code is working, the first request is a GET request, which accesses the form, will go directly to the else block.
Once the user puts up a search query, results will be shown, which will be a post request, and 2nd if block will be activated, this request we will store in a session.
When the user accesses 2nd search page, it will be a GET request, but we are checking whether there is an active pagination session, and also checking whether it's not a page request of GET. At this point 1st if block will be trigerred.
def search(request):
if not request.method == "POST" and 'page' in request.GET:
if 'search-query' in request.session:
request.POST = request.session['search-query']
request.method = 'POST'
if request.method == 'POST':
form = Search_form(request.POST)
request.session['search-query'] = request.POST
if form.is_valid():
search_query = form.cleaned_data.get('search_query')
search_parameter = form.cleaned_data.get('search_parameter')
print(search_query, search_parameter)
queryset_list = CompanyRecords.objects.filter(**{f'{search_parameter}__icontains': search_query}).exclude(
company_name__isnull=True).exclude(description__isnull=True).exclude(phones__isnull=True).exclude(
emails__isnull=True)[:5]
page = request.GET.get('page', 1)
paginator = Paginator(queryset_list, 2)
try:
queryset = paginator.page(page)
except PageNotAnInteger:
queryset = paginator.page(1)
except EmptyPage:
queryset = paginator.page(paginator.num_pages)
return render(request, 'search/search_results.html', {'queryset': queryset})
else:
context = {
'form': Search_form()
}
return render(request, 'search/search.html', context)

Categories

Resources