How to set cookie in Django and then render template? - django

I want to set a cookie inside a view and then have that view render a template. As I understand it, this is the way to set a cookie:
def index(request):
response = HttpResponse('blah')
response.set_cookie('id', 1)
return response
However, I want to set a cookie and then render a template, something like this:
def index(request, template):
response_obj = HttpResponse('blah')
response_obj.set_cookie('id', 1)
return render_to_response(template, response_obj) # <= Doesn't work
The template will contain links that when clicked will execute other views that check for the cookie I'm setting. What's the correct way to do what I showed in the second example above? I understand that I could create a string that contains all the HTML for my template and pass that string as the argument to HttpResponse but that seems really ugly. Isn't there a better way to do this? Thanks.

This is how to do it:
from django.shortcuts import render
def home(request, template):
response = render(request, template) # django.http.HttpResponse
response.set_cookie(key='id', value=1)
return response

The accepted answer sets the cookie before the template is rendered. This works.
response = HttpResponse()
response.set_cookie("cookie_name", "cookie_value")
response.write(template.render(context))

If you just need the cookie value to be set when rendering your template, you could try something like this :
def view(request, template):
# Manually set the value you'll use for rendering
# (request.COOKIES is just a dictionnary)
request.COOKIES['key'] = 'val'
# Render the template with the manually set value
response = render(request, template)
# Actually set the cookie.
response.set_cookie('key', 'val')
return response

response = render(request, 'admin-dashboard.html',{"email":email})
#create cookies
expiry_time = 60 * 60 #in seconds
response.set_cookie("email_cookie",email,expiry_time);
response.set_cookie("classname","The easylearn academy",expiry_time);

def index(request, template):
response = HttpResponse('blah')
response.set_cookie('id', 1)
id = request.COOKIES.get('id')
return render_to_response(template,{'cookie_id':id})

Related

Publish a custom Django Flatpage at a set date and time

I have a custom Flatpage model:
from django.contrib.flatpages.models import FlatPage
class MyFlatPage(FlatPage):
publish = models.DateTimeField()
so that I can add a publish date in the future.
Now, I don't have a proper list of flatpages on the front end, my use for frontpages is more like 'one-offs', where I specific the URL and all that. For example, 'about', '2019prize', 'Today's walk', stuff like that.
The urls.py is set up to catch all the flatpages with:
from django.contrib.flatpages import views
re_path(r'^(?P<url>.*/)$', views.flatpage)
How can I set these pages I create to be displayed only after the publish date has arrived? I know that I can filter them by looking up something like pages.filter(publish__lte=now). Where and how should I put that code though?
Additional information
I suppose I need to create a custom view, is that correct? The original view is in ../lib/python3.8/site-packages/django/contrib/flatpages/views.py:
def flatpage(request, url)
if not url.startswith('/'):
url = '/' + url
site_id = get_current_site(request).id
try:
f = get_object_or_404(FlatPage, url=url, sites=site_id)
except Http404:
if not url.endswith('/') and settings.APPEND_SLASH:
url += '/'
f = get_object_or_404(FlatPage, url=url, sites=site_id)
return HttpResponsePermanentRedirect('%s/' % request.path)
else:
raise
return render_flatpage(request, f)
#csrf_protect
def render_flatpage(request, f):
if f.registration_required and not request.user.is_authenticated:
from django.contrib.auth.views import redirect_to_login
return redirect_to_login(request.path)
if f.template_name:
template = loader.select_template((f.template_name, DEFAULT_TEMPLATE))
else:
template = loader.get_template(DEFAULT_TEMPLATE)
f.title = mark_safe(f.title)
f.content = mark_safe(f.content)
return HttpResponse(template.render({'flatpage': f}, request))
How can I extend this, adding my if publish__lte=now code?
What I did is copy-paste the view code from ../lib/python3.8/site-packages/django/contrib/flatpages/views.py to my app.views, rename the two functions, and add the following to render_myflatpage:
def render_myflatpage(request, f):
[...]
if f.publish > now:
f.content = 'This content will be published on ' + str(f.publish)
I then assigned the new view in the catch-all urls.py code:
re_path(r'^(?P<url>.*/)$', myflatpage)
I know this goes against the DRY protocol; this works for me for the time being. If there's a more elegant solution please do let me know.

Django redirect by calling function view?

I need to redirect while keeping the original request. Is this approach inline with Django 3 logic or am I creating some furute catastrofe?
def final_view(request):
# use some_new_variable to do stuff and then display it in the template
return render(request, another_template_name, {})
def original_view(request):
# do stuff
request.some_new_variable = "x"
return final_view(request)
Not sure exactly what you're trying to accomplish, but you could always pass "some_new_variable" via the URL
For instance:
views.py
def original_view(request):
x = # do your stuff here
return redirect('final_view_url', var=x)
def final_view(request, var)
final_var = # do some additional stuff with var here
return render(request, another_template_name)
urls.py
path('/final_view/<var>', views.final_view, name="final_view_url")
So you send your original request to "original_view", do some stuff, then pass that variable into the URL to "final_view"

Django not displaying correct URL after reverse

There's lots of documentation about Django and the reverse() method. I can't seem to locate my exact problem. Suppose I have two urlconfs like this:
url(r'ParentLists/$', main_page, name = "main_page"),
url(r'ParentLists/(?P<grade_level>.+)/$', foo, name = "foo")
and the two corresponding views like this:
def main_page(request):
if request.method == 'POST':
grade_level = request.POST['grade_picked']
return HttpResponseRedirect(reverse('foo', args = (grade_level,)))
else:
return render(request, 'index.html', context = {'grade_list' : grade_list})
def foo(request, grade_level):
grade_level = request.POST['grade_picked']
parent_list = # get stuff from database
emails = # get stuff from database
return render(request, 'list.html', context = {'grade_list' : grade_list, 'parent_list' : parent_list})
Here, list.html just extends my base template index.html, which contains a drop down box with grade levels. When the user goes to /ParentLists, the main_page view renders index.html with the drop down box as it should.
When the user picks a grade level from the drop down box (say 5th Grade), the template does a form submit, and main_page once again executes - but this time the POST branch runs and the HttpResponseRedirect takes the user to /ParentLists/05. This simply results in an HTML table pertaining to grade 5 being displayed below the drop down box.
The problem is, when the user now selects say 10th Grade, the table updates to show the grade 10 content, but the URL displayed is still /ParentLists/05. I want it to be /ParentLists/10.
Clearly, after the first selection, the main_page view never executes again. Only foo does...and so the HttpResponseRedirect never gets called. How should I reorganize this to get what I'm looking for? Thanks in advance!
As you correctly mentioned you will never redirect to foo() from foo().
So the simple way to fix this is just add similar code as in main_page() view:
def foo(request, grade_level):
if request.method == 'POST':
grade_level = request.POST['grade_picked']
return HttpResponseRedirect(reverse('foo', args = (grade_level,)))
else:
parent_list = # get stuff from database
emails = # get stuff from database
return render(request, 'list.html', context = {'grade_list' : grade_list, 'parent_list' : parent_list})
Please note that I remove grade_level = request.POST['grade_picked'] because as Nagkumar Arkalgud correctly said it is excessively.
Also instead of combination of HttpResponseRedirect and reverse you can use shortcut redirect which probably little easy to code:
from django.shortcuts redirect
...
return redirect('foo', grade_level=grade_level)
I would suggest you to use kwargs instead of args.
The right way to use the view is:
your_url = reverse("<view_name>", kwargs={"<key>": "<value>"})
Ex:
return HttpResponseRedirect(reverse('foo', kwargs={"grade_level": grade_level}))
Also, you are sending "grade_level" to your view foo using the URL and not a POST value. I would remove the line:
grade_level = request.POST['grade_picked']
as you will override the grade_level sent to the method from the url.

How to get /test/?grid-view or /test/?list-view' in Django view

I have a page that can be viewed by list or grid view through jQuery, but I want the URL to display what view it is -- especially when user paginate so that they don't have to reclick what they want to view -- so something like this: /test/?grid_view or /test/?list_view.
I've been trying to use request.GET.get, but it doesn't seem to be working. Here is what I have so far:
def test (request):
grid_view = request.GET.get('grid_view')
list_view = request.GET.get('list_view')
if grid_view:
return render_to_response('grid_view.html', {}, context_instance=RequestContext(request))
elif list_view:
return render_to_response('list_view.html', {}, context_instance=RequestContext(request))
else:
return render_to_response('default_view.html', {}, context_instance=RequestContext(request))
Then in my main template I'd point the different views to list view, etc. There is probably a better way to do it so any suggestions are appreciated too.
An empty string is evaluated to False and, that is what your GET request Query String parameters will contain.
You could modify your code to:
if 'grid_view' in request.GET or 'grid_view/' in request.GET:
pass
elif 'list_view' in request.GET or 'list_view/' in request.GET:
pass
Or:
if request.GET.has_key('grid_view') or request.GET.has_key('grid_view/'):
# ...
That's not a GET parameter. Use request.META['QUERY_STRING'] to get the query string.

Django: custom 404 handler that returns 404 status code

The project I'm working on has some data that needs to get passed to every view, so we have a wrapper around render_to_response called master_rtr. Ok.
Now, I need our 404 pages to run through this as well. Per the instructions, I created a custom 404 handler (cleverly called custom_404) that calls master_rtr. Everything looks good, but our tests are failing, because we're receiving back a 200 OK.
So, I'm trying to figure out how to return a 404 status code, instead. There seems to be an HttpResponseNotFound class that's kinda what I want, but I'm not quite sure how to construct all of that nonsense instead of using render_to_response. Or rather, I could probably figure it out, but it seems like their must be an easier way; is there?
The appropriate parts of the code:
def master_rtr(request, template, data = {}):
if request.user.is_authenticated():
# Since we're only grabbing the enrollments to get at the courses,
# doing select_related() will save us from having to hit database for
# every course the user is enrolled in
data['courses'] = \
[e.course for e in \
Enrollment.objects.select_related().filter(user=request.user) \
if e.view]
else:
if "anonCourses" in request.session:
data['courses'] = request.session['anonCourses']
else:
data['courses'] = []
data['THEME'] = settings.THEME
return render_to_response(template, data, context_instance=RequestContext(request))
def custom_404(request):
response = master_rtr(request, '404.html')
response.status_code = 404
return response
The easy way:
def custom_404(request):
response = master_rtr(...)
response.status_code = 404
return response
But I have to ask: why aren't you just using a context processor along with a RequestContext to pass the data to the views?
Just set status_code on the response.
Into your application's views.py add:
# Imports
from django.shortcuts import render
from django.http import HttpResponse
from django.template import Context, loader
##
# Handle 404 Errors
# #param request WSGIRequest list with all HTTP Request
def error404(request):
# 1. Load models for this view
#from idgsupply.models import My404Method
# 2. Generate Content for this view
template = loader.get_template('404.htm')
context = Context({
'message': 'All: %s' % request,
})
# 3. Return Template for this view + Data
return HttpResponse(content=template.render(context), content_type='text/html; charset=utf-8', status=404)
The secret is in the last line: status=404
Hope it helped!