Possible to send 2 querysets to response? - django

render_to_response Is it possible to pass more variables than just one? For example in my application I have a members model and then i would like to display the members information and also attendance information. Would I have to supply the arguments as a tuple?
Thanks in Advance,
Dean

Render_to_response accepts a context which is used for rendering. As far as I know there is no limitation on the number of variables you can pass in the context. This includes QuerySets. For example:
def my_view(request, *args, **kwargs):
# ... etc ...
q1 = Model1.objects.filter(**conditions)
q2 = Model2.objects.filter(**conditions)
context = dict(q1 = q1, q2 = q2)
return render_to_response('my_template.html', context_instance = RequestContext(request, context))
My example uses RequestContext but it should be fine without it too.
# Template
{% for foo in q1 %} {{ foo }} {% endfor %}
... stuff ...
{% for bar in q2 %} {{ bar }} {% endfor %}

While Manoj is correct that you can pass variables by constructing your own context instance and passing it as a keyword argument to render_to_response, it's often shorter/simpler to use the second positional argument to render_to_response which accepts a dictionary that gets added to the context behind the scenes.
Take a quick look at the docs for render_to_response. Their example usage looks like this (and allows you to pass anything that can be stored in a dict to the renderer):
from django.shortcuts import render_to_response
def my_view(request):
# View code here...
return render_to_response('myapp/index.html', {"foo": "bar"},
mimetype="application/xhtml+xml")

Related

Django Annotations - More efficient way?

In a class Based ListView I want to annotate each Post with the number of comments (this method works)
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['approved_comments'] = Comment.objects.values('post_id').annotate(c_count=Count('post_id')).filter(approved=True)
return context
in my Template I do the following (which feels really inefficient)
{% for comment in approved_comments %}
{% if post.id == comment.post_id %}
{{comment.c_count}}
{% endif %}
{% endfor %}
This gets me the result I want but I'm struggling to find a better way to deliver this since this seems really redundant.
I was trying to find a SO question that already deals with this but haven't found one so if you could point me to the link that would be helpful.
Thanks in advance :)
This is a quadratic JOIN, you should not do that. You can simply annotate the Posts with the number of approved comments in the view:
from django.db.models import Count, Q
from django.views.generic import ListView
class MyListView(ListView):
model = Post
queryset = Post.objects.annotate(
napproved_comments=Count('comment', filter=Q(comment__approved=True))
)
and then render this in the template with:
{{ post.napproved_comments }}

django redirect to another view with context

I have this in my view
def foo(request):
context['bar'] = 'FooBar'
return redirect('app:view')
is there some way to include this context['bar'] when I redirect to 'app:view'? My last resort and alternative would be to do use render() however, I do not want to define all the context variables again. Is there any other approach to this?
I would use session variables in order to pass some context through a redirect. It is about the only way to do it outside of passing them as part of the url and it is the recommended django option.
def foo(request):
request.session['bar'] = 'FooBar'
return redirect('app:view')
#jinja
{{ request.session.bar }}
A potential pitfall was pointed out, whereas the session variable gets used incorrectly in a future request since it persists during the whole session. If this is the case you can fairly easily circumvent this problem in a future view in the situation it might be used again by adding.
if 'bar' in request.session:
del request.session['bar']
In django You can not pass parameters with redirect. Your only bet is to pass them as a part of URL.
def foo(request):
context['bar'] = 'FooBar'
redirect(reverse('app:view', kwargs={ 'bar': FooBar }))
in your html you can get them from URL.
you need to use HttpResponseRedirect instead
from django.http import HttpResponseRedirect
return HttpResponseRedirect(reverse('app:view', kwargs={'bar':FooBar}))
I was with the same problem. I would like to redirect to another page and show some message, in my case, it's a error message. To solve it, I used the django messages: https://docs.djangoproject.com/en/4.0/ref/contrib/messages/
I did this:
def foo(request):
message.error(request, 'bla bla bla')
return redirect('foo_page')
In my template foo_page.html:
{% if messages %}
{% for message in messages %}
<div class={{ message.tags }}>{{ message }}</div>
{% endfor %}
{% endif %}
What most work for me is:
next_url = '/'
url = reverse('app:view')
url = f'{url}?next={next_url}&'
return redirect (url)
I just had the same issue, and here is my solution.
I use Django messages to store my parameter.
In template I do not read it with template tag {% for message in messages %}, but rather do POST request to my API to check if there are any messages for me.
views.py
def foo(request):
messages.success(request, 'parameter')
return redirect('app:view')
api/views.py
#api_view(['POST'])
#login_required
def messageList(request):
data = {}
messageList = []
storage = messages.get_messages(request)
for message in storage:
msgObj = makeMessage(message.message, tag=message.level_tag)
messageList.append(msgObj['message'])
data['messages'] = messageList
return Response(data)

Pagination and get parameters

Django 1.11.4
I have build a search form with method="get".
Search form has a lot of forms.
Then this input values are transmitted as get parameters in url.
The problem is how to get pagination. The database contains thousands of objects. Pagination is necessary.
This is what the documentation tells us:
https://docs.djangoproject.com/en/1.11/topics/pagination/#using-paginator-in-a-view
It suggests like this:
previous
But this will ruin all the get parameters.
What I have managed to invent is:
previous
This works. But this is goofy. If one switches pages forward and backward, it produces urls ending like this:
page=2&page=3&page=2
I have had a look at how Google managed this problem. In the middle of the url they have start=30. And change this parameter: start=20, start=40. So, they switch.
Could you help me understand how preserve get parameters and switch pages in Django? In an elegant way, of course.
The generic solution is to define a "custom template tag" (a function) which keeps the complete URL but updates the GET parameters you pass to it.
After registration, you can use this function in your templates:
previous
To define and register the custom template tag, include this code in a python file:
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def query_transform(context, **kwargs):
query = context['request'].GET.copy()
for k, v in kwargs.items():
query[k] = v
return query.urlencode()
*Thanks to Ben for the query_transform code. This is an adapation for python 3 from his code.
Why this method is better than reconstructing the URLs manually:
If you later decide that you need additional parameters in your URLs: 1. you don't have to update all the links in your templates. 2. You don't need to pass all the params required for recostructing the URL to the templates.
Typically, to preserve GET parameters you simply re-write them manually. There shouldn't be many cases where having to do this will matter.
&page={{page}}&total={{total}}
You can abstract this away into a template include or a custom template tag.
https://docs.djangoproject.com/en/1.11/howto/custom-template-tags/
Additionally, you could create a string filter that takes URL params as a string as well as a dict of values you want to change. The filter could then parse the params string, update the value, then recombine the string back into URL params.
{{ request.get_full_path | update_param:'page=8' }}
This is what I did and find easier. Many not be better approach though!
param = ""
if search_programs:
qs = qs.filter(title__icontains=search_programs)
param = param + f"&search_programs={search_programs}"
if level:
qs = qs.filter(level__icontains=level)
param = param + f"&level={level}"
if category:
qs = qs.filter(sector__icontains=category)
param = param + f"&category={category}"
paginator = Paginator(qs, 16)
page_number = self.request.GET.get('page')
page_obj = paginator.get_page(page_number)
context = {
'page_obj': page_obj,
"param": param
}
return render(self.request, self.template_name, context)
# in html
next
Subclassing
I solved this problem by subclassing Django's Paginator class and adding the stuff I needed there.
You need to override the init method to allow yourself to pass the request and form objects from the view, so that you can retrieve the request.GET parameters and compare them to form.fields. You want to only allow GET parameter names that are predefined in your form. Otherwise you remove them.
Once you have subclassed the Pagination class, you can use it in your views.py files, passing the extra request and form parameters to it. Then you can render your template as suggested in the Django documentation, but now you have access to the paginator.first_params, paginator.last_params, page.previous_params, and page.next_params variables that you can use.
pagination.py
import urllib
from django.core.paginator import Paginator as DjangoPaginator, EmptyPage
class Paginator(DjangoPaginator):
def __init__(self, *args, **kwargs):
request = kwargs.pop('request', None)
form = kwargs.pop('form', None)
super().__init__(*args, **kwargs)
self.params = {key: value for key, value in request.GET.items() if key in form.fields.keys()}
self.first_params = urllib.parse.urlencode({**self.params, 'page': 1})
self.last_params = urllib.parse.urlencode({**self.params, 'page': self.num_pages})
def get_page(self, *args, **kwargs):
page = super().get_page(*args, **kwargs)
try:
page.previous_page_number = page.previous_page_number()
except EmptyPage:
page.previous_page_number = None
try:
page.next_page_number = page.next_page_number()
except EmptyPage:
page.next_page_number = None
page.previous_params = urllib.parse.urlencode({**self.params, 'page': page.previous_page_number})
page.next_params = urllib.parse.urlencode({**self.params, 'page': page.next_page_number})
return page
views.py
from .pagination import Paginator
paginator = Paginator(queryset, per_page, request=request, form=form)
page = paginator.get_page(page_number)
pagination.html
{% if page %}
<nav id="pagination">
{% if page.has_previous %}
« First
Previous
{% endif %}
<span class="current">
Page {{ page.number }} of {{ page.paginator.num_pages }}.
</span>
{% if page.has_next %}
Next
Last »
{% endif %}
</nav>
{% endif %}

How do I disable Django's autoescape from a view?

Django says there's 3 ways to turn off autoescape:
Use |safe after the variable
Use {% autoescape on %} and {% endautoescape %} within blocks
Use a Context like context = Context({'message': message}, autoescape=False)
(1) and (2) work fine. But I have the situation where I have templates to generate plain-text push notifications, and I have LOADS of templates to build and maintain. I could go through and put the {% autoescape on %} and {% endautoescape %} tags in all of them, but (3) should allow me to do it in one line in the view.
The template:
{% block ios_message %}{{message}}{% endblock %}
The view:
message = u"'&<>"
context = Context({'message': message}, autoescape=False)
render_block_to_string(template_name, 'ios_message', context)
The output:
u''&<>
The code for block_render.py is from here: https://github.com/uniphil/Django-Block-Render/blob/master/block_render.py. I'm using it as is from there.
Anyone know what gives?
Take a closer look to function render_block_to_string():
def render_block_to_string(template_name, block, dictionary=None,
context_instance=None):
"""Return a string
Loads the given template_name and renders the given block with the
given dictionary as context.
"""
dictionary = dictionary or {}
t = _get_template(template_name)
if context_instance:
context_instance.update(dictionary)
else:
context_instance = Context(dictionary)
return render_template_block(t, block, context_instance)
The 3rd arg should be a dict, not context. Otherwise it would use the normal context instance.
So I believe it should be:
render_block_to_string(template_name, 'ios_message', {}, context)
Hope it helps.
I could solve it my doing it like that:
from django.template.context import make_context
from django.template.loader import get_template
# Getting the template either by a path or creating the Template object yourself
template = get_template('your/path/to/the/template.html')
# Note here the 'template.template' and the 'autoescape=False' parameter
subject = template.template.render(make_context(context, autoescape=False))
Found it by doing that myself. Because by default the autoescape setting will be used from the engine
https://github.com/django/django/blob/4b6dfe16226a81fea464ac5f77942f4d6ba266e8/django/template/backends/django.py#L58-L63
Django Version: 2.2.1
Wanted to comment but looks like I don't have enough reputation since this is a newish account
You can turn off the autoescape found here: https://github.com/django/django/blob/529c3f264d99fff0129cb6afbe4be2eb11d8a501/django/template/context.py#L137
i.e.
Context(data_dict, autoescape=False)

Sending an object with request

In the below code i am trying to send a object with the request,Is this correct if so how to decode it in template
def index(request):
cat = Category.objects.filter(title="ASD")
dict = {'cat' : cat}
request.update('dict' : dict)
#or
request.dict=dict;
And In the templates can we write the code as
{% for obj in request.dict%}
obj.title
{% endfor %}
EDIT:
If i am calling function like
def post_list(request, page=0, paginate_by=20, **kwargs):
logging.debug("post_list")
page_size = getattr(settings,'BLOG_PAGESIZE', paginate_by)
return list_detail.object_list(
request,
queryset=Post.objects.published(),
paginate_by=page_size,
page=page,
**kwargs
)
You could do this, but why would you want to? Django has a simple, well-defined and well-documented way of passing data into templates - through the context. Why try and find ways to work around that?
Edit after comment No. Again, Django has a perfectly good way of passing extra context into a generic view, via the extra_context parameter which again is well-documented.
You're not showing the actual function you use to render your view (render(), render_to_response(), etc.).
Let's say you are using render_to_response() :
render_to_response(template_name[, dictionary][, context_instance][, mimetype])
Renders a given template with a given
context dictionary and returns an
HttpResponse object with that rendered
text.
So if you pass in {"foo": your_object} as a dictionary you can use {{ foo }} directly in your template.
If you are using the object_list generic view you should use the extra_context:
extra_context: A dictionary of values
to add to the template context. By
default, this is an empty dictionary.
If a value in the dictionary is
callable, the generic view will call
it just before rendering the template.